Four Kitchens
Insights

Base64 encoding images with Node.js

4 Min. ReadDevelopment

If you’re a follower of this blog or Four Kitchens in general you might have noticed that making the web accessible to mobile devices is something we’re interested in. One of the biggest challenges with mobile computing is unreliable network connections that are inevitably encountered in the wild. This is particularly troublesome when your website becomes more of a web application that should behave the same regardless of connection status. There are many, many issues that crop up when the wire goes dark, but today we’re going to focus on one strategy for dealing with images, and a solution we created to help with this.

As web developers we’re all familiar with the <img> tag for embedding images in the document. Normally the source element of this tag points to a file on the server that the browser will fetch during a page load process. The source element can also contain what’s called a data URI. Data URIs allow you to embed base64 encoded data directly in the HTML document. In the case of image tags this means we can embed all data for an image in the document. A 1×1 gif file looks like this when it’s base64 encoded:

<img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7">

Modern browsers will then interpret this data and display images as usual, avoiding another call to the server to fetch the image.

So what does this have to do with offline applications? Base64 encoding images and keeping them inline does two things:

  1. Reduces the number of calls to the server that the client needs to make.
  2. Moves the caching of these images into the cache entry for the document. If the document was cached by the browser, it’ll work offline and future loads won’t require server communication to fetch images. (Note: for simplicity’s sake I’m purposefully ignoring any JavaScript or CSS that might also be required for your application to function correctly offline).

It’s not all fun and games from here though, there are tradeoffs for base64 encoding images. Notably: file sizes tend to be a bit larger for base64+gzipped images than they would be for their binary+gzipped counterparts. There can also be a higher CPU cost on initial page loads while the browser processes the inline images. These problems can be mitigated by being careful about which images you base64 encode based on your use case.

So, now that we know about base64 encoded images and what they’re good for, let’s look at a way to replace non-base64 encoded images in an HTML document. There are lots of tools to do this, many of them online, but let’s make this more interesting and assume that we need to do the replacements on the fly before they’re served to clients. Let’s say that we have a RSS feed that contains embedded images that needs to have its contents parsed and distributed to client applications.

If you’re using node.js as a server backend or preprocessor of some sort, this is now incredibly easy thanks to the img64 module. img64 uses jsdom and jQuery to look for images in strings and replaces the source references with base64 encoded data URIs. Here’s an example of doing this:

var img64 = require('img64');
var string = "<p>Organic mattis pharetra eget lorem Brooklyn eget mattis sodales donec keytar vitae magna ut ipsum +1 sagittis. Congue proin auctor viral commodo pellentesque sem donec vinyl tellus sapien justo urna Austin non eros eros curabitur you probably haven't heard of them.</p><img src=\"http://upload.wikimedia.org/wikipedia/commons/2/23/1x1.GIF\" />";

img64.encodeImgs(string, function encoded(err, doc) {
  // Voici! Our 1x1 gif is now base64 encoded and
  // embedded as a data uri! The string is otherwise
  // unchanged.
  console.log(doc);
});

Now you can include content from anywhere in your web application without needing to worry about whether or not the content provider has already base64 encoded their images!

In our example case this means that the HTML delivered to the browser will now contain base64 encoded images for all the feed content, regardless of its origin. In addition to the advantages of base64 encoding images we outlined above this also means that the images hosted on other servers will not need to be fetched and downloaded. Assuming you have permission to use these images this means your application will not need to be concerned if an image’s host goes down or an image is moved, etc: your app will always behave the same as long as the base64 encoded image is present.

The img64 module was created as a proof of concept and while it’s definitely a viable solution it hasn’t been battle tested yet. Check it out; if it works for you, awesome! If not, open a pull request. Happy encoding!