In the past, I've used base64-encoded data URIs to show client-side image previews. However, in a recent revamp of InVision App, Jonathan Rowny started rendering image previews using a relatively new browser technology - Object URLs. Rather than reading the image into memory and converting it into another format (base64), Object URLs allow us to link directly to the file on the user's local system. This has both memory and performance considerations. And, since I've never played with this myself, I wanted to put together a quick little demo.
In the following demo, I've created a component directive that provides a dropzone. When you drop images onto the dropzone, the image previews are rendered in a list. The directive can render the image using either the older base64 data URI approach or the newer Object URL approach. In fact, the difference in access patterns is so minimal, it can be easily encapsulated behind the promise API.
That said, the URL.createObjectURL() method is actually synchronous. Since it's not really doing any processing - just providing a local URL - it can return immediately. The base64 data URI, on the other hand, needs to read the file into memory and then encode it as a string. As such, it is necessarily an asynchronous promise. To keep things uniform, however, I am putting both access patterns behind a promise such that the consuming code can assume async access for both approaches.
Once an Object URL is created using URL.createObjectURL(), it is recommend that you subsequently deallocate the url, when you no longer need it, using URL.revokeObjectURL(). Apparently this is for memory management; however, when using the Chrome DevTools Profiler, I wasn't seeing anything concerning when I ran the demo without explicit deallocation. That said, I'm all for best practices; so, in the following code, I keep track of the URLs as I generate them and then deallocate them in the $destroy event.
With all that said, let's take a look at the code. The first instance of the component directive uses Object URLs (as per the preview-method="url" attribute) and the second instance of the component directive uses base64 data URIs (as per the preview-method="base64" attribute):
As you can see, in either approach, the link() function takes care of managing the dropzone and extracting the image preview URL. Then, regardless of the preview technique, the link() function lets the controller know about the new image to render as part of the view-model. And, when we run this code, you can see what a local Object URL looks like:
If you watch the video, or you play around with demo, you'll likely see that the Object URL approach is faster than the base64 approach. This difference is more pronounced in some browsers (Firefox) and less pronounced in other browsers (Safari). But, the support is pretty good (IE10+).
This is some pretty cool stuff. And, with decent support. Since the difference in workflow between Object URLs and base64 data URIs is insignificant, I don't see any reason to not try to use Object URLs by default and then fallback to base64 data URIs in slightly older versions of IE. It wouldn't even be a lot of code - throw it behind a promise and you're ready to rock!
Want to use code from this post? Check out the license.