Managing image uploads in your Browser: Base64 vs ObjectURLs

Scott Cook
Level Up Coding
Published in
5 min readFeb 6, 2020

--

Images rule the internet with over two billion pics making their way to the web every day. Because images are such an integral part of the web experience, browsers and HTML have a few “built in” tools to handle and process images efficiently. I’m going to walk through the best way(s) to import, manage, and display your images in a browser.

Here’s an example from my project, watermarks.io. Try it out.

A web browser is shown with a user importing 20 megabytes of photos
A sample from my site, watermarks.io. 20mb worth of photos in < 300ms

Let’s get our hands dirty and learn how to make speedy imports like the example above.

Part 1: will cover the import and then ways to store image data.

Part 2: will cover asynchronous multi-image imports (coming soon)

tl;dr is all the way at the bottom :)

The Basics — Importing an Image

I’m going to explain this as simply as possible, because the fundamentals are necessary in order to understand and build more robust solutions for imports.

Your browser is allowed to access a computer’s local filesystem through the File interface. Each File is stored in aFileList object that is populated by a user interacting with the <input type="file> element. The File objects in FileList are simply references to the file in your local filesystem.

Remember, the File object is only a reference to the actual file on your computer, so to get the actual data of that file to work with in the browser, we need to “import” it using the FileReader().

Let’s step through an example below.

All browser imports (of any file type) must be imported through an <input> element, in our case with a type="file" attribute. You can add the accept="image/*" attribute to notify your Mac Finder or Windows Explorer to only show images as options for import. When a user selects a file to add, the onchange method of <input> is triggered and our function importFileandPreview() is run.

<input type="file" onchange="importFileandPreview()"><br>
<img src="" height="200" alt="Image preview...">

Below, we add an Event Listener to our FileReader() represented by reader.

You can access the user uploaded file (w/ JS) by grabbing it using the DOM Selector document.querySelector('input[type=file]').files[0];

Once you have a reference to that file, myFile you will pass it to the FileReader using the readAsDataUrl() method. This method will read the contents of the file the user uploads and store the ingested data as a Base64 DataUrl. We will touch heavily on what a DataUrl is in the next section of this article.

Once the readAsDataURL method has finished reading the contents of our local file, it is “converted” into a DataUrl which is stored in Memory for our JS to use. In most cases, once a File is read as a DataUrl, the src of an image element is set to the DataUrl to show or preview the file.

var reader  = new FileReader();
var myFile = document.querySelector('input[type=file]').files[0];
reader.addEventListener("load", function () {
// do something with reader.result
console.log(file.name)
}, false);

reader.readAsDataURL(myFile);
}

note: my examples are not intended for copy paste, if you want runnable code, use the complete CodePen examples

Try importing an image

Base64 (DataURL) vs ObjectURL

In the above import, we imported a binaryFile object and represented it as a Base64 encoded string (DataURL) that the browser can easily interpret. One thing to note is that Base64 encoded images are ~33% larger than the original file size.

Note that Base64 encoded images are ~33% larger than the original file size.

If you are dealing with a large number of high quality images, your browser (and JS) will quickly become overburdened by DataURL’s due to their increased size. Imagine you have a 20mb image imported to the browser. If it’s encoded as a DataURL, that is a 26.6mb *string* that is now being managed through your Web App. Any action you do on it, means you’re moving around a massive string which is bad for performance and bad for memory management.

A better way to deal with multiple large images in your browser is by using ObjectURL’s.

An ObjectURL creates a URL (ie. reference) to an object (of File type or Blob). In the case of an image, an ObjectURL creates a URL reference to the original File object. And best of all, an <img> element can use ObjectURL’s directly to display images.

File objects cannot be used directly to display images. However, ObjectURL’s can represent a File object directly and can be used as the src of an <img> element.

  • Recall that when a user imports an image, it is initially a File object. The browser cannot display the File object directly which is why it needs to be encoded as either a DataURL or represented as an ObjectURL.

The setup for importing an image and representing it as an ObjectURL is very similar to the example at the beginning of this post:

The magic here is window.URL.createObjectURL(file); This creates the ObjectUrl which you can use as the src element anywhere in your app. Best of all, its easy to pass around this small string from widget to widget, to an HTML Canvas, or for a Server Upload.

Compare the src attribute length from this pen to the first DataUrl pen! ObjectURLs are much lighter and are recommended for large files and multiple imports.

tl;dr

Browsers love images and there’s a couple ways to show local file system images. Files/Images cannot be sourced directly from the browser, so they need to be “imported” or encoded for viewing in the browser. The two ways to do this:

  1. You can display a DataURL. DataUrl’s are Base64 encoded strings of the original image that are HUGE. All of the data is contained in the Base64 string which cause their total size to be inflated by ~33%. These massive strings are stored in JS memory and are bad for performance, especially when moving the image data around your application or to a server.
  2. You can use ObjectURL’s. In a very oversimplified sense, ObjectURL’s are browser created “local” URL references to the file in your local file system. These URLs are accessing the files binary data, so the image data itself is not inflated. The ObjectURL’s can easily be passed around your WebApp, to an HTML Canvas, or for server upload. ObjectURL’s are the way to go! Just make sure to revoke the URL if you are done using/displaying an image.

Feel free to try out image uploads in app.watermarks.io and inspect the src attribute after upload. They are clean and tidy ObjectURL’s that are blazing fast.

Next up, I will cover asynchronous multi-image imports in Part II

Cheers!

--

--