HTML5 File API
Jakob Jenkov |
The HTML5 file API enables JavaScript inside HTML5 pages to load and process files from the local file system. Via the HTML5 file API it is possible for JavaScript to process a file locally, e.g. compress, encode or encrypt it, or upload the file in smaller chunks. Of course the HTML5 file API raises some security concerns. This HTML5 file API tutorial will explain both how to use the file API, and what the security constraints on the HTML5 file API are.
Core Objects of HTML5 File API
The HTML5 file API contains the following core objects:
FileListFileBlobFileReader
The File object represents a file in the local file system.
The FileList object represents a list of files in the local file system. For instance, a list of
files inside a directory.
The Blob object represents a Binary Large OBject (BLOB) which is used to hold the contents of a single
file from the local file system.
You will see how these file API objects are used in the following sections.
Selecting Files
Before the HTML5 file API can access a file from the local file system, the user has to select the file to give
access to. For security reasons selecting files is done via the <input type="file"> HTML element.
Here is an input element example:
<input type="file" >
By itself the input element is not enough. You need an onchange listener too. Here
is how that looks:
<input type="file" onchange="readFiles(event)" >
The input element contains a browse button. When that button is clicked the user
is shown a file dialog. In that file dialog the user can choose a file. The chosen file will be made available
to the onchange listener via the event object passed as parameter to it.
Selecting Multiple Files
To enable the user to select multiple files, insert the multiple attribute into the input
element. Here is how that looks:
<input type="file" onchange="readFiles(event)" multiple>
Selecting Files Via Drag and Drop
You can combine the HTML5 file API with the HTML5 drag and drop feature. By doing so the user can drag files from a file explorer on his / her computer (outside the browser) onto a "drop zone" HTML element inside the HTML page. The HTML page can detect when a file is dropped via JavaScript.
Here is a code example showing how to setup a drop zone HTML element so that you can detect when files are dragged onto it:
<div id="fileDropZone" >
Drag file in here
</div>
<script>
function drop(event) {
evt.stopPropagation();
evt.preventDefault();
var fileList = event.dataTransfer.files;
// access files via fileList
}
function dragOver(evt) {
evt.stopPropagation();
evt.preventDefault();
evt.dataTransfer.dropEffect = 'copy';
}
var dropZone = document.getElementById("fileDropZone");
dropZone.addEventListener("dragover", dragOver, false);
dropZone.addEventListener("drop" , drop , false);
</script>
The two functions dragOver() and drop() are set as drag and drop event listeners on
the drop zone HTML element (the div at the beginning of the example).
When a file is dropped onto
the drop zone HTML element, the drop() function will be called. The list of files dragged onto the
drop zone element can be found in the event.dataTransfer.files object (a FileList object).
The dragOver() function is not strictly necessary. It just shows that the file being dragged is copied.
This is shown via the dragged file icon.
The rest of the code needed to access the selected files is the same as when selecting a file via the input
element. You will see how that looks in the following sections.
It might be a good idea to style the drop zone HTML element to make it clear to the user that they can drag a file onto it, and where the borders of the drop zone are etc. I have left the styling out of the example above to make it shorter.
Accessing Selected Files
Once the user has selected one or more files, there are two ways to access the selected files.
The first way to access the selected files is by accessing the input field's files
property. Here is an example of that:
var inputField = document.getElementById('theFileInput');
var selectedFiles = inputField.files;
for(var i=0; i<selectedFiles.length; i++) {
var file = selectedFiles[i);
}
The second way to access the selected files is via an onchange listener
function on the input field. The selected files are accesible via the event object passed
as parameter to the listener function. Here is an example showing both an input element and an
onchange listener function:
<input type="file" onchange="readFiles(event)" multiple>
<script>
function readFiles(event) {
var fileList = event.target.files;
}
</script>
The event.target.files object is a FileList object. It contains a list of the
selected files as File objects. If only a single file is selected, this list contains only one object.
Here is an example showing how to iterate the file list:
<input type="file" onchange="readFiles(event)" multiple>
<script>
function readFiles(event) {
var fileList = event.target.files;
for(var i=0; i < fileList.length; i++ ) {
var file = fileList[i]; // a File object
console.log("i: " + i + " - " + file.name);
}
}
</script>
The FileList is a list of File objects. These File objects are used to
access the files with.
In the following sections you will see different ways to load the files via JavaScript.
Loading Files With a FileReader
Once the user has selected a file and you have a reference to the selected file (e.g. via the onchange
event) you can read the file using a FileReader. The FileReader object contains the
following functions you can use to load files with:
readAsText()readAsDataUrl()readAsArrayBuffer()
Each of these four functions take either a File object or a Blob object as parameter.
A File object can be obtained from a FileList object, as explained earlier.
A Blob object represents a part (or whole) of a file. You create a Blob object from
a File object like this:
var theFile = fileList[0]; // a File from a FileList var from = 3; var to = 9; var blob = theFile.slice(from, to); // create Blob
The from index represents the index of the first byte in the file to include in the Blob.
the to index represents the index of the first byte that will not be included in the
Blob. Thus, if the to index is 9 as in the example above, the byte with index 8 is the
last byte to be included in the Blob .
Creating a FileReader
To use a FileReader you must first create a FileReader object. Here is how you create
a FileReader object:
var reader = new FileReader();
Once the FileReader has been created you can call the various read functions on it.
Loading a File as Text
In order to load a file as text you use a FileReader object which is part of the HTML5 file API.
Here is how loading a file via a FileReader looks:
<input type="file" onchange="readFiles(event)" multiple>
<script>
function readFiles(event) {
var fileList = event.target.files;
for(var i=0; i < fileList.length; i++ ) {
loadAsText(fileList[i]);
}
}
function loadAsText(theFile) {
var reader = new FileReader();
reader.onload = function(loadedEvent) {
// result contains loaded file.
console.log(loadedEvent.target.result);
}
reader.readAsText(theFile);
}
</script>
The file is loaded inside the loadAsText() function. First a FileReader
object is created.
Second, an onload event handler function is set on the FileReader
object. This event handler function is called when the file is finished loading.
Third, the readAsText() function on the FileReader is called with the File as
parameter. This starts the loading of the file. When loading the file finishes, the onload event
handler is called.
Loading a File as Text Slice
Instead of loading the whole file as text you can load a slice of the text instead. Here is an example function called
loadAsTextSlice() which shows how to use the HTML5 file API to load a file as a text slice:
function loadAsTextSlice(theFile) {
var start = 3;
var stop = 9;
var blob = theFile.slice(start, stop);
var reader = new FileReader();
reader.onload = function(loadedEvent) {
console.log(loadedEvent.target.result);
}
reader.readAsText(blob);
}
First the example creates a Blob object via the File object's slice()
function. Second, a FileReader is created and an onload handler is set on the
FileReader instance.
Third, the FileReader's readAsText() function is called
with the Blob object as parameter. This starts the reading of the file. Once the file slice is read
the onload event handler function is called. The text slice from the file is available in the
loadedEvent.target.result variable.
Loading a File as Data URL
It is also possible to load a file as a data URL. A data URL can be set as source (src) on
img elements with JavaScript. Here is a JavaScript function named loadAsUrl() that shows
how to load a file as a data URL using the HTML5 file API:
function loadAsUrl(theFile) {
var reader = new FileReader();
reader.onload = function(loadedEvent) {
var image = document.getElementById("theImage");
image.setAttribute("src", loadedEvent.target.result);
}
reader.readAsDataURL(theFile);
}
First a FileReader is created. Second, an onload event handler is set on the
FileReader. Third, the readAsDataURL() function is called on the FileReader.
When the file has finished loading the onload event handler function will get called. The
event.target.result property of the event object passed as parameter to the onload
event handler function contains the file encoded as a data URL. You can now set the loaded and encoded file as
src attribute value of the img element you want to show the image.
Load a File as ArrayBuffer
The readAsArrayBuffer() function of the FileReader object can be used to read a file
as an ArrayBuffer. An ArrayBuffer represents an arbitrary length buffer of data.
You cannot directly manipulate the data in the ArrayBuffer. Instead you have to create a DataView
object which can access the data in the ArrayBuffer as different types, e.g as number of 8, 16 or 32 bits
in size.
Here is an example of reading a file as an ArrayBuffer:
function loadAsUrl(theFile) {
var reader = new FileReader();
reader.onload = function(loadedEvent) {
var arrayBuffer = loadedEvent.target.result;
var dataView = new DataView(arrayBuffer, 0, arrayBuffer.byteLength);
var byte = dataView.getUint8(0); //gets first byte of ArrayBuffer
//... process rest of dataView ...
}
reader.readAsArrayBuffer(theFile);
}
Here you can read more about the DataView object .
Upload File via Fetch
It is possible to upload a file accessed via the HTML5 File API via the JavaScript AJAX fetch() function.
Here is an example of how uploading a file via fetch() looks:
<form id="theForm">
<input type="file" id="fileField" >
<input type="button" value="Upload" onclick="upload(event)">
</form>
<script>
function upload(event) {
console.log("Uploading...");
const fileField = document.getElementById("fileField");
const formData = new FormData();
formData.append('test', 'testValue');
formData.append('selectedFile', fileField.files[0]);
fetch('https://mydomain/upload', {
method: 'PUT',
body: formData
})
.then((result) => {
console.log('Success:', result);
})
;
}
</script>
Monitoring Progress of File Loading
The HTML5 file API also enables you to monitor the progress of file loading, meaning you can be notified about how much of the file
has been loaded. To be notified you need to set an onprogress listener function on the FileReader
instance
var reader = new FileReader();
reader.onprogress = function(progressEvent) {
if(progressEvent.lengthComputable) {
var percentLoaded = Math.round( (
progressEvent.loaded * 100) / progressEvent.total );
}
console.log("total: " + progressEvent.total + ", loaded: "
+ progressEvent.loaded + "(" + percentLoaded + "%)");
}
The progress event objects passed to the progress listener function contains a boolean property named
lengthComputable. If this property has the value true then you can compute how much of
the total file has been loaded. How that is done is shown above.
Browser Support for HTML5 File API
According to caniuse.com the HTML5 file API is reasonably well supported in most browsers. A few
browsers do not support the File constructor, but you don't need that.
| Tweet | |
Jakob Jenkov | |











