Thursday, October 20, 2011

Caching Images in a Mobile Web app

Another post on a technique to help make mobile Web apps work offline.

Last time I showed how to use XMLHttpRequest and local storage to download and cache Javascript scripts. Here I'm going to show how to download and cache an image, and then inject it dynamically into your HTML page.

That technique can be particularly useful to optimize an image intensive app (a game or a photo gallery app for example) and allow it to work offline.

First place base64-encoded (as defined in Internet RFC 3548) versions of your images on your Web server. On Mac OS X for example, convert foo.png to a base64-encoded foo.b64 file like this:
$ base64 -w 0 foo.png >foo.b64

Configure your Web server to serve .b64 files as text/plain. If you're using the Apache httpd 2.x Web server -- and if you're not using it, you should :) -- add the following to your httpd.conf server configuration file:
text/plain b64

The reason why we want to convert our images to base64-encoded is that it'll make it much easier to use them in the HTML5 app pages. We're getting to that part now.

Place the following Javascript script in your HTML page, under the <head> element for example:
<script type="text/javascript">
window.appcache = {};

// Get a resource
window.appcache.get = function(url) {
  var doc = localStorage.getItem(url);
  if (doc != null)
    return doc;
  var http = new XMLHttpRequest();
  http.open("GET", url, false);
  http.send(null);
  if (http.status != 200)
    return null;
  localStorage.setItem(url, http.responseText);
  return http.responseText;
}

// Convert an image to a data: URL
window.appcache.img = function(url) {
  var b64 = window.appcache.get(url);
  return 'data:image/png;base64,' + b64;
}
</script>


I'm not showing the error handling code to keep it short, but you get the picture. The get(url) function downloads a resource and caches it in local storage. The img(url) function gets an image resource and converts it to a data: URL. A data: URL (as defined in Internet RFC 2397) allows you to include resource data (here our base64-encoded image data) immediately in the URL itself.

Now, later in your HTML page, say you have an <img/> element like this:
<img id="foo"/>

You can set the cached image into it dynamically, like this:
<script type="text/javascript">
var foo = document.getElementById('foo');
foo.src = window.appcache.img('/foo.b64');
</script>


The image src property will recognize the data: URL, the image/png;base64 media type at the beginning of the URL, and read the base64-encoded content as a PNG image.

That's it. With the little trick I've described here you should now be able to:
  • control the download of your images;
  • cache them in local storage for speed and working offline;
  • inject them dynamically into your HTML page as needed.

Hope this helps. In the next few days I'll show how to handle CSS style sheets in a similar way.

2 comments:

Petr said...

Great article! Very useful.
Petr.

Petr said...
This comment has been removed by the author.

The postings on this site are my own and don’t necessarily represent positions, strategies or opinions of my employer IBM.