HTML 5 Offline data storage

One of the most anticipated features of HTML 5 and all it's consequential technologies and APIs are the «offline storage» APIs. In this post I'll take a "real world" approach to using the HTML 5 Web-Database and the «HTML 5 Offline application cache». I will take you through an example where a complete elephant gets stored on your computer. That's right! A pink one as well! The point of this demo is to investigate how to store both static data as well as dynamic data which might not be known at design time. For this occasion I have created a demo which you are free to download and inspect. It is fully commented and somewhat verbose for easier reading. What this application does in essence is to store a HTML and a JavaScript file on your computer, then it will go on to store a picture (which could be dynamic data) in a local database, hence making the application usable when not online. Let's have a look!

So, our goal is to store both static data and some dynamic data. To achieve this we'll need to use two approaches as mentioned above. The first one, called «HTML 5 Offline Application Storage» is almost automatic once you have it configured. We will utilize this method to store our main JavaScript file so that it will work when you're not online. Cool!
This approach uses a simple text file, called a «manifest file» which tells the browser which data to store locally. In order for this file to be interpreted by the browser correctly you'll need to (1) configure your web-server to serve this file with the «text/cache-manifest» mime-type. It doesn't matter what kind of extension you use, but I prefer either «.manifest» or «.cache». Now that thats out of the way we need to create the «manifest file». Mine looks something like this.

# Cache manifest version 1.0.5
# If you change the version number in this comment,
# the cache manifest is no longer byte-for-byte
# identical.


# All URLs that start with the following lines
# are whitelisted.

The first line MUST be the text «CACHE MANIFEST». After that you'll go on to specify the files you'll like to be cached. Here you'll put stuff like JavaScript files, HTML pages, CSS files and so on. Mine only has the one «main.js». Note that you do NOT need to specify the HTML file which declares the manifest file. This will typically be your «index.html» file or something like that. More on that later. The next section is the «NETWORK:» section. In this section you specify a "white list" containing URL's from where your application is allowed to get it's data. If you don't specify this your app will not download any data, not from the server it's hosted on or from anywhere else. I have specified my own domain since this is where the elephant is hosted.
You declare the manifest file in your HTML file like so:
<html manifest="cache.manifest">
That is it for the manifest file actually. If you are simply going to host a static site, a game or something like that, this will work just file as is. Note that to update your files, you do need to make a change in the manifest file itself, like incrementing the version number.

Next we need to add some sexy JavaScript in order to make the pink elephant available for your viewing pleasure in locations the WiFi gods have forsaken. There are three main steps to this: (1) Opening and creating the database and the table if it's not already present. (2) Reading the image from the database or saving it to the database if it's not already in there. (3) Displaying the image. I will not be explaining every line of the code in this post, you'd rather take a look at the JavaScript file and read the comments. However I will discuss some of the more important points briefly.
Not all browsers will support the HTML 5 database APIs, so we need to check for this before we can do anything. To do this we check for the existence of the «openDatabase» method on the «window» object, like so:

// No support for HTML 5 db

This will detect if the browser has support for the methods we need. If not, give the user a message or some alternative content.
Then, to open / create the database we would we simply write:
db = openDatabase('testdb','1.0','Offline Elephant DB',1024*1024);
We have just created a 1MB database or opened one if it already existed. Now it's ready to execute SQL queries using transactions.

var sql = 'CREATE TABLE IF NOT EXISTS offline_image (id INTEGER ....);';
  function(transaction, result){
   //The table was created

The database table has now been created if it wasn't already there. We now need to check if there is an image already saved in the database, this happens on line 56 in the «readImage()» function. If there is an image with the matching filename saved, we use that, if not we go on to loading and serializing the image, as follows.

var canvas = document.createElement('canvas');
var ctx    = canvas.getContext('2d');
var img    = document.createElement('img'); 
img.onload = function(){
 canvas.width  = img.width;
 canvas.height = img.height;
 var base64Image = canvas.toDataURL();
img.src = sImgURL;

To be able to save the image we need to create a text version of it's data. We can accomplish this by loading the image and then rendering it in a canvas element. The Canvas element has a method called «toDataURL» which will create a base64 representation of the Canvas content. Base64 images, also referred to as data urls can easily be saved to the database. Base64 data can also be read directly by the «img» element, so there is no need to decode the base64 string again once it's encoded. But, do note that the canvas element will give you the png base64 version of the image, and it's quite a bit larger than the original binary file.

That's the gist of it, but you do need to take a look at the demo and the JavaScript in order to really understand what it going on here. Once you get the hang of it, this is a really powerful approach to making web-applications much more accessible and more interesting.

Further reading:


Anonymous said...

man that was a good one..really

Eero said...

Excellent post! Any idea how much larger the png in base64 will be? 10 times?

Jørn Kinderås said...

It's actually more like 1.37 times the size of the original file, or 137%. This according to the Base64 spec.