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:
FileList
File
Blob
FileReader
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 |