Introduction
Retrieving a File
object from a file-selection interface is cool. We can extract out meta information about the file and use that to do numerous things - such as lay out file type checks, file size checks and so on.
But wouldn't it be even more amazing if we could somehow utilise the actual content of the file too! Imagine that we select an image using a file input and then pass its data to an <img>
element, to be displayed on-the-go.
In this chapter, we shall learn about a convenient method of generating URLs that point to given File
objects, ultimately serving the purpose of reading the content of those files. It is URL.createObjectURL()
.
In the coming chapter on the FileReader()
API, though, we will dive a lot deeper in file reading, exploring the formats and techniques of the utility.
Creating URLs
The global URL
object comes equipped with many methods to aid in URL processing. One of those, which we shall explore in this section, is the createObjectURL
method.
URL.createObjectURL()
serves to create URLs that point to given File
objects (or better to say Blob
objects, as we will see in the next chapter).
The method takes as argument the file object to generate a URL for.
Once provided, the file object is internally mapped to a unique URL and finally this URL is returned; which can then be passed to anything that requires a source link.
For example the returned URL can be provided to the src
attribute for <img>
and <script>
tags, or the href
attribute for <a>
and <link>
tags.
As can be inferred, the return value of URL.createObjectURL()
is of type "string"
.
URL.createObjectURL()
is called without an argument, or with one that's not a File
object, the method will throw a TypeError
exception.Consider the code below:
<input type="file" id="fileInput"><br>
<img id="preview">
var fileInput = document.getElementById("fileInput");
var preview = document.getElementById("preview");
fileInput.onchange = function(e) {
preview.src = URL.createObjectURL(this.files[0]);
}
We have an input
element to select a file and an img
element for previewing that file. The onchange
handler of the input element is configured to change the src
of the image and point it to the newly-selected file (using a URL string which maps to it).
As you see here, URL.createObjectURL()
is an extremely useful method to map any file object to a particular URL and then use that URL in places where URl strings are required.
In the same way, we used the method to create image file URLs here, you can use it for literally any other file!
Just make sure that whatever file you choose, to create a URL for, has some HTML element which can utilise it.
For example, creating an object URL for a Python file or a PHP file would be meaningless because no HTML element can parse these file types. In contrary, doing the same for PDF files would be fruitful as we can display PDF files in the <iframe>
element.
Anyways, following is a task to check whether you can pin point a problem in the code above!
There's a problem in the code above, that will cause an exception to be thrown.
Determine this problem, and rewrite the code above to solve it.
Hint: Remember 'empty' input
s?
The problem is that of an empty input.
If the user selects no file in the file browsing interface, then the input
element would become empty with no elements in the files
property.
This will cause the statement URL.createObjectURL(this.files[0])
to through an exception, since this.files[0]
would evaluate to undefined
which is an unacceptable value for the method.
We just need to check the files
property, in some way or other, and proceed only if it has an element in it. Following is one way to solve the code above:
var fileInput = document.getElementById("fileInput");
var preview = document.getElementById("preview");
fileInput.onchange = function(e) {
var file = this.files[0];
if (file) {
preview.src = URL.createObjectURL(this.files[0]);
}
}
In another variant, we can check the length
property of the files
object and proceed if it isn't 0
.
There's still a problem in the code above, which can cause even unsupported file URLs to be added to the src
attribute of the <img>
element.
Determine this problem, and rewrite the code above to solve it.
Hint: Supported file objects contain the substring "image"
in their type
property!
The problem is that when we select a file in the file input
element, it might not be an image file - such as an HTML file, a CSS file or any other file.
In such a case, we can't assign the file's URL to the src
attribute of the <img>
element, since it can't parse non-image files!
What we need to do instead, is to first check the type
of the selected file and proceed only if it contains the "image"
substring in it. This is because all image files have a MIME type which begins with image/
.
Following is the corrected code:
var fileInput = document.getElementById("fileInput");
var preview = document.getElementById("preview");
fileInput.onchange = function(e) {
var file = this.files[0];
if (file && file.type.indexOf("image") !== -1) {
preview.src = URL.createObjectURL(this.files[0]);
}
}
Depending on your case, you can give an else
block here as well to deal with the unsupported file.
Revoking objects
So uptil this point we know that an object URL can be created by calling the URL.createObjectURL()
method, and it can be passed to anything which requires source links.
However, note that an object URL is only valid upto the point its corresponding File
data is in memory.
That is, unless the file is revoked (cleared away from memory), either explicitly or implicitly, the URL can continue pointing to the file without any problem.
Otherwise the URL will fail!
Implicit revoking, also known as automatic revoking, is performed automatically by the browser the moment we close the browser tab. The corresponding File
's data is cleaned up from the memory and likewise subsequent calls to the URL fail.
On the other hand, explicit revoking, also known as manual revoking, is accomplished using the URL.revokeObjectURL()
method.
In most cases, manual revoking isn't usually necessary. However, in some cases it's definitely worth the effort as it helps optimise resources by garbage-collecting unwanted file objects from memory.
URL.revokeObjectURL()
takes an argument which is the object URL whose associated file we wish to revoke.
In the example that follows, we use this method to free up memory, each time a new file is selected in the input
element, by garbage-collecting the previous file.
var fileInput = document.getElementById("fileInput");
var preview = document.getElementById("preview");
var prevURL, newURL;
fileInput.onchange = function(e) {
// first clear the previous URL
URL.revokeObjectURL(prevURL);
// then create a new one
newURL = URL.createObjectURL(this.files[0]);
preview.src = newURL;
// now the new URL becomes the previous URL
prevURL = newURL;
}
URL.revokeObjectURL()
here is described as follows:
Select an image file, then once it's displayed, right-click on it and choose Open in new tab
. This will open the image object in a new tab. Let this tab remain opened, and then go to the file input interface and select another file.
Once this is done, go back to the previously opened tab and refresh it - you'll encounter an error saying something like 'No file found'. This happens because with the selection of a new file, the previous file object is cleared away from memory.
In one of their examples, MDN utilises URL.revokeObjectURL()
very effectively - in the onload
handler for image elements, to clean up the image file as soon as it's loaded!
Following we demonstrate a simpler version of their example:
var fileInput = document.getElementById("fileInput");
var preview = document.getElementById("preview");
var newURL;
preview.onload = function(e) {
// once loaded, remove the image object from memory
URL.revokeObjectURL(prevURL);
}
fileInput.onchange = function(e) {
// create an object URL
newURL = URL.createObjectURL(this.files[0]);
preview.src = newURL;
}
The first two lines are the same as in the code above, so there's nothing new to share in there! In the third line, however, we remove the declaration for prevURL
just because it isn't needed here.
Moving on, we assign an onload
handler to the preview
image element whose use we'll discuss shortly below.
After this, in fileInput
's onchange
handler, we create an object URL out of the selected file and then assign it to the src
attribute of preview
.
The moment this image loads, and therefore displays completely, the onload
handler, we created before, takes over. It merely removes the image's data from memory by calling URL.revokeObjectURL(newURL)
.
load
event occurs, it isn't at all necessary to keep it in memory!Obviously, as with the code snippets in the previous section, this code snippet also has some errors in it which need to be rectified before implementing it on deployment level. To spot the errors, do Task 1 and Task 2.
What's the problem in the code below?
var fileInput = document.getElementById("fileInput");
var preview = document.getElementById("preview");
var prevURL, newURL;
fileInput.onchange = function(e) {
var file = this.files[0];
if (file && file.type.indexOf("image") !== -1) {
URL.revokeObjectURL(file);
newURL = URL.createObjectURL(file);
preview.src = newURL;
prevURL = newURL;
}
}
- A
File
object is passed toURL.revokeObjectURL()
- There's no problem