Introduction
In this chapter we shall begin with getting some intuition behind the File
API in JavaScript, particularly looking into how to use it to find useful information about a given file. Let's begin...
Accessing a file in JavaScript
There are primarily two ways to access a file, from the local filesystem, in JavaScript and they are as follows:
- Selecting a file using an
input
element withtype="file"
- Dragging and dropping a file in a drop zone.
In both cases, the selected files can be accessed via the files
object, on the input
element and the dataTransfer
property of the DragEvent
argument.
Following we demonstrate both these cases to ultimately retrieve a File
object, for each of the files we select.
Using the input
element
When working with a file input
element, it's quite common to give it an onchange
handler which can respond to the action of browsing and then selecting a file in it.
Consider the code below:
<input type="file" id="fileInput">
var fileInput = document.getElementById("fileInput");
fileInput.onchange = function(e) {
// we'll understand this property just shortly!
var files = this.files;
}
When you initially interact with the resulting file input interface here i.e browse your local filesystem and finally select a file, you indirectly perform the change
event and thus cause the respective onchange
handler to fire.
input
element's value to change - hence the name change
for the corresponding event!In this case the handler saves the list of all the files selected in a local variable files
, from the files
property on the input
element.
The files
property of a file input
element is a FileList
object which holds a File
object for each of the selected files.
The most important thing to realise here is that - the files
property is accessible through the input
element, which is the this
value inside the onchange
handler shown above.
In the next section we shall see what is hidden under every File
object.
Using the drag-and-drop API
Apart from using the input
element to allow a user to browse and ultimately select a file, another way to accomplish this is using the drag-and-drop API.
The idea is simple - the user picks up a file outside the browser, drags it into the browser window and finally drops it into a special area known as a drop zone.
The drop zone is made to respond to drop actions and consequently extract information out of the dropped items. This is done by handling the drop
and dragover
events occuring on the respective element made the drop zone.
Let's implement a simple drag-and-drop utility:
First we create an element to be converted into a drop zone.
<div id="drop-zone"></div>
Then we give this element the ondragover
and ondrop
event handlers (to turn it into a drop zone).
var dropZone = document.getElementById("drop-zone");
dropZone.ondragover = function(e) {
// prevent the default behavior
e.preventDefault();
}
dropZone.ondrop = function(e) {
// prevent the default behavior
e.preventDefault();
var files = e.dataTransfer.files;
}
e.preventDefault()
calls here are extremely important! Omitting them would resume the default dropping behavior, where the dropped file will be displayed in the browser window.Inside the ondrop
handler (in line 8), we use the event argument e
to access the dropped file.
This event argument contains a dataTransfer
property that holds information about the dropped item. If we go further down the dataTransfer
object, we notice a files
property.
This files
property operates exactly the same way as the one on the input
element, we saw above. It's also a FileList
collection with individual File
objects containing information about each of the dropped files.
Now that we know where to start accessing files in JavaScript, it's time that we dive right into inspecting the individual file objects.
Inspecting a file
As highlighted earlier, the files
property holds a list of all the files selected using one of the file selection UIs discussed above. The individual elements in this list are all File
objects.
So what is a File
object and how does one look like?
In simple words:
File
object represents a file, fetched from the local filesystem.A File
object is used to hold the actual data of a file. Along this data, it also contains some useful information about the file.
But what information exactly? Essentially the following:
- Name of the file
- Its size, in bytes
- Its MIME type, for example
text/plain
for text files,text/html
for HTML files and so on.. - Its Last Modified date
Consider the following example to get a real overview of the information held on by a File
object.
Suppose we select a file foods.txt
, using the HTML input interface, with the information shown below:
Size: 562 Bytes
Last Modified: Fri, 23 Aug 2019 16:45:49
The files
property of the input
element will return us a FileList
object with a File
object for each of the selected files.
Since we're assuming we've have selected only one file, it will be accessible via referring to the first element of the FileList
object, at index 0
.
The resulting File
object will have the following properties:
name
with the value"foods.txt"
size
with the value562
type
with the value"text/plain"
lastModifiedDate
holding aDate()
object to denote the date shown in the snippet above.lastModified
corresponding to the number of milliseconds since the the Unix Epoch to thelastModifiedDate
.
The first three properties are fairly handy to us.
For example, we can show what file a user selected by displaying its name using name
; or ensure that a file doesn't exceed a certain size limit by checking size
or that it's only one of some given file types by checking type
.
The last two aren't completely useless, but, at least, not on the same level as the former set of properties.
So now that even the File
API is examined, why not move over to consider some real practical examples, and in the way also explore some of the methods on FileList
.
Practical examples
First we'll see how to monitor file changes in an input
element and log information for the new file selected.
The process is very simple - handle the change
event on the input element, retrieve the first element in its files
property, and finally extract information out of it.
<input type="file" id="fileInput">
var fileInput = document.getElementById("fileInput");
fileInput.onchange = function(e) {
// retrieve the first File element
var file = this.files[0];
console.log("name:", file.name);
console.log("size:", file.size);
console.log("type:", file.type);
}
files
property (in line 5), is that we've omitted the multiple
attribute on the input
element. This means that only one file is allowed to be selected at a time, which in turn means that files
will be a list with at most one element.Now imagine that we select the foods.txt
file, we saw in the section above, with this code in place. The console will then look something similar to the following:
The properties are equal to exactly what we had expected them to be equal to in the section above.
Next, let's consider a slightly more complicated example.
We'll see how to process multiple file objects selected using an input
element, to display all their information in a table.
Consider the following code:
<input type="file" id="fileInput" multiple>
<table>
<thead>
<tr><th>Name</th><th>Type</th><th>Size</th></tr>
</thead>
<tbody id="desc"></tbody>
</table>
Very simply, we have an input
element with the multiple
attribute on, to select multiple files; and a table
to show all their data.
The scripting part goes as follows:
var fileInput = document.getElementById("fileInput");
var tableBody = document.getElementById("desc");
fileInput.onchange = function(e) {
var files = this.files;
var html = [];
// iterate over all files
for (var file of files) {
// generate row markup for each file
html.push(`<td>${file.name}</td><td>${file.type}</td><td>${file.size}</td>`);
}
// finally create all table rows
// and add them the tbody element
tableBody.innerHTML = '<tr>' + html.join('</tr><tr>') + '</tr>';
}
The input
element is selected in fileInput
and assigned an onchange
handler to take care of all the display logic.
Whenever one selects a bunch of files, the change
event fires and consequently the onchange
handler shown above gets fired.
Inside the handler, we iterate over all the elements in the files
object of the input element using the for...of
loop, and deal with each File
element separately.
For each File
element, we retrieve its name
, size
and type
, and put these inside a formatted markup string, and finally push this string onto an html
array.
The reason of doing so is to avoid string concatenation - it's relatively inefficient as compared to pushing all the strings in an array and then concatenating them just once!
Even in our example, we weren't concatenating a lot of files. Nonetheless we used the array method only to demonstrate how to make even simple-looking programs more efficient!
Finally, we join all the required markup and place it inside the <tbody>
element.
This completes our, so-called, complicated example. You might probably agree that it wasn't that complicated, do you?
Construct a file input interface that only accepts a single file less than or equal to 200KB.
If the file is less than 200KB, log "OK"
otherwise log "Limit Exceeded"
.
The property size
holds the size of the selected file, in units of bytes. Therefore, in one way, we check size
against the value 200000
(200 * 1000
).
<input type="file" id="fileInput">
var fileInput = document.getElementById("fileInput");
fileInput.onchange = function(e) {
var file = this.files[0];
if (file) {
if (file.size <= 200000)
console.log("OK");
else
console.log("Limit Exceeded!");
}
}
In line 5, we check file
in order to ensure that a file is actually selected in the input
element while we are in the onchange
handler.