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:

  1. Selecting a file using the input element, set to type="file"
  2. 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 how to accomplish both these cases, to ultimately get 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.

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 handler to fire.

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.

Just think of it: when you select a new file, you get the input element's value to change - hence the name change for the corresponding event!

Perhaps, this is the most important thing to realise here - 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 this files 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:

<div id="drop-zone"></div>

First we create an element to be converted into a drop zone.

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 behaviour
    e.preventDefault();
}

dropZone.ondrop = function(e) {
    // prevent the default behaviour
    e.preventDefault();

    var files = e.dataTransfer.files;
}
The e.preventDefault() calls here are extremely important! Omitting them would resume the default dropping behaviour, 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, a File object represents an actual file, fetched from the filesystem.

A File object is used to hold the data of a file. Along this data, it also contains some useful information about the file.

But what information exactly? Essentially the following:

  1. Name of the file
  2. Its size, in bytes
  3. Its MIME type, for example text/plain for text files, text/html for HTML files and so on..
  4. 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:

Name: foods.txt
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 containing the following properties:

  1. name with the value "foods.txt"
  2. size with the value 562
  3. type with the value "text/plain"
  4. lastModifiedDate holding a Date() object, denoting the date shown in the snippet above.
  5. lastModified corresponding to the number of milliseconds since the the Unix Epoch to the lastModifiedDate.
Read more about UTC date strings at JavaScript Dates.

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);
}
The reason we select the first element here in the files property, is that we've omitted the multiple attirbute on the input element. This causes only one file to be allowed at a time.

Now imagine that we select the foods.txt file above with this code in place. The console will then look something similar to the following:

name: foods.txt
size: 562
type: text/plain

Next, let's consider a slightly more complicated example.

We'll see how to process multiple file objects selected using an input element and 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>
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>';
}

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 an iterator, and deal with each element separately.

For each File element, we retrieve its name, size and type, and append these to the table's markup; in this case using a template string (line 11).

Then we push them into the html array. The reason of doing so is to notify you to avoid string concatenation in complex cases, where many many strings are to be joined together - it's relatively inefficient as compared to pushing all the strings in an array and then concatenating them just once!

Don't rewrite all your string concatenation is bad in itself - only avoid it when you think you're about to do a lot of concatenation!

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 file less than 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, to convert it into kilobytes (KB) we have to divide it by 1000.

Once this is done, we can check the result against the value 200 and then operate likewise.