Recently I came across an API from a client that delivered images in part of the response as Base64 encoded strings. My immediate reaction was “Bad idea”.
Here are the concerns we discussed which led the API to be changed to use URIs for images instead.
At the end I consider a couple of cases where Base64 encoding could be a good option.
API response payloads are large.
By including the data for every image in the API response, the response payloads started to get in the MB range.
Over mobile networks, additional payload sizes matter. Larger payloads also mean more time for responses to be in flight and more time for (mobile) network drops.
Base64 images are bloated.
As well as a larger overall payload size, the size of the Base64 encoded image is approximately 30% larger than the original binary image.
With gzip compression this bloat can be avoided if the alternative is raw binary images. However sizes will still be larger if the binary images are compressed. At scale these things start to matter. (See some stats here: http://bit.ly/2o9eYWx.)
User Experience is negatively impacted .
By including all image data together in one API response, the app must receive all data before drawing anything on screen. This means users will see on-screen loading states for longer and the app will appear sluggish as users wait.
Alternatively, when an app loads images separately from the API payload, the app can show the data from the API and then progressively load the images. The effect is a more responsive app that feels faster to users.
CDN caching is harder.
Contrary to image files, the Base64 strings inside an API response cannot be delivered via a CDN cache. The whole API response must be delivered by CDN.
An API caching strategy can be straightforward if the API is not dynamic, e.g. personalised to each customer. However, with dynamic APIs there is quite a bit of architectural complexity compared to delivering static assets such as images via a CDN.
Image caching on the device is no longer possible.
Typically apps will cache images so they must not be repeatedly re-fetched. However, if images are sent to the device as Base64 encoded strings every time within an API response, any caching capabilities on the device becomes irrelevant.
Caching on the device represents quite a major improvement for app performance, especially as app-level caching is very easy with mature libraries such as Picasso for Android (http://bit.ly/2C4JjPm) or Kingfisher for iOS (http://bit.ly/2EDtGQL).
Content management becomes harder.
Most content management tools handle images as binary files. To create Base64 encoded representations there will probably need to be some post-processing following image upload. This might require some custom logic that is hooked into your content management system. You will also need to watch for any adverse effects on database performance depending on your storage engine.
If you are storing large volumes of images, then you are shifting a bulk of storage to a structured datastore (i.e. a database). With cloud computing, file-based storage is very cheap and there might be a cost implication for taking a Base64 approach to images.
Does Base64 encoding images ever make sense on mobile?
Yes, it can. Here are a couple of ideas.
The Dropbox team make a compelling case for using Base64 encoding to effectively stream thumbnails for an image gallery: http://bit.ly/2BDnfdK. The argument is that when loading many small images, the overhead of HTTP requests becomes a factor. Key to their approach is using chunked transfer encoding which effectively streams the response so the client can process the Base64 encoded images as they arrive across the wire. Also significant is that the use-case is centred around fetching images — in other words this isn’t for delivering images as part of a mixed content API.
Another use could be delivering slow-changing non-personalised data that includes small images. For example, loading partner logos, map pin images or category icons during app launch/restore. APIs delivering this type of data can easily be cached on a CDN and versioned to trigger updates. Whether the gains for slow-changing data warrant the development effort is another matter.
There are likely to be other scenarios where Base64 encoded images are a smart approach. For me, these are going to be special cases. Most of the time, simply loading images via a URI is the way to go.