Johannes Thönes

Software-Developer, ThoughtWorker, Permanent Journeyman, Ruby Enthusiast, Java and Devils Advocate.

HTML5: Offline Upload of Images

I am currently working on an application which has needs to work offline. This has the beneficial side effect, we use the different HTML5 storage capabilities. One of the is the File API, which we are using to store images locally – before queuing them for upload to a backend server.

In this article, I will share some code how we did this. The example works in Google Chrome – for DOM manipulation I will use JQuery.

Starting simple, we have one file input for images and an area to show the uploaded images.

1
2
3
4
<body>
 <input type="file" accept="image/*" class="js-image-upload"/>
 <div class="js-image-container"></div>
</body>

When the user select a file, we want to store the image.

1
2
3
4
5
6
$(document).on('change', '.js-image-upload', function (event) {
  var file = event.target.files[0];
  var fileName = createTempName(file);

  writeImage(fileName, file);
});

The image storage is handled by this method.

1
2
3
4
5
6
7
8
9
10
11
function writeImage(fileName, file) {
  getFileSystem(function (fileSystem) {
    fileSystem.root.getFile(fileName, {create: true}, function (fileEntry) {
      fileEntry.createWriter(function (fileWriter) {
        fileWriter.onwriteend = writeSuccessFull;
        fileWriter.onerror = errorFct;
        fileWriter.write(file);
      }, errorFct);
    });
  });
}

What is happening here?

  • Retrieve the file system
  • Create a file by the specificied name on its root
  • Create a writer for this file
  • Configure a success and error callback when the asynchronous file write happend
  • Write the blob of the file using the writer

The retrieval of the file system is a two step procedure. We need to request quota from the browser and than get the file system.

1
2
3
4
5
6
var SIZE = 100 * 1024 * 1024; // 100 MB
var getFileSystem = function (successFct) {
  navigator.webkitPersistentStorage.requestQuota(SIZE, function () {
    window.webkitRequestFileSystem(window.PERSISTENT, SIZE, successFct, errorFct);
  }, errorFct);
};

The user will be asked to grant the website the access to a persistent storage. There are some errors you can get, e.g. when the user does not accept our request.

But let’s assume the user trusts us. Then we want to react to the successful write and show the image. We can use a local file storage url and add the file to a queue to upload the file to the server.

1
2
3
4
5
6
7
8
9
10
var showImage = function (fileName) {
  var src = 'filesystem:' + window.location.origin + '/persistent/' + fileName;
  var img = $('<img />').attr('src', src);
  $('.js-image-container').append(img);
};

var writeSuccessFull = function () {
  addToSyncQueue(fileName);
  showImage(fileName);
};

I’m omitting the queue logic here. You can keep a queue of images for uploaded in the web storage or IndexedDB of your application. To read the image from storage you can use something like this

1
2
3
4
5
6
7
8
9
10
var readImage = function (fileName, successFct) {
  getFileSystem(function (fileSystem) {
    fileSystem.root.getFile(fileName, {}, function (fileEntry) {

        fileEntry.file(successFct, errorFct);

      }, errorFct);
    }
  );
};

So this is a brief overview of what we did here. The working example code can be found here: https://gist.github.com/jthoenes/3668856a188d600e02d6

Hope it has been useful to a few people dealing with similar issues. Feel free to ask questions, when something pops up in your mind.