Client-Side Operations with Local Files

Part 2. FileReader

So far we have found the way to gain access to the local file system and extract main file attributes. Now it's time to determine the ways to get the file contents. The instrument that will afford necessary functionality is the FileReader. The interface provides a convenient mechanism for reading files and representing their contents in various formats:

  • it allows a client script to read a file as a plain text;
  • it obtains the binary representation of a file, so the raw data can be analyzed, transformed, encrypted, etc.;
  • it is capable of transforming a file into a properly encoded data: URL.

The FileReader interface is asynchronous: a reading operation returns control to the main thread of the browser without waiting for the operation to complete. The asynchronous model combined with events chaining keeps the front-end UI safe from 'freezing'.

FileReader Events Model

All read operations are performed in a series of interconnected events. Events flow is monitored by the appropriate event handlers. When a read operation begins, the loadstart event is fired. If the file to read is sizable, progress events are dispatched each time a FileReader instance reads a new chunk of data: the chunk is called partial file data and can be analyzed on-the-fly within the scope of progress handlers. If an error occurs, an error event handler is activated. In this case the FileReader's error property will hold the error details. If the read operation is interrupted by the user, an abort event handler is alerted. When reading flow proves uninterrupted, the load event signals the successful completion of the read. The loadend event terminates the chain irrespective of success or failure.

loadstart progress load
error
abort
loadend

The FileReader implements the EventTarget interface, so it enables registration and removal of event listeners. Event listeners accept an object implementing both Event and ProgressEvent interfaces. An Event instance contains contextual information about the event, its target property is the FileReader itself. The ProgressEvent interface members reflect the flow of read operations.

Any implementation of the ProgressEvent interface makes available the lengthComputable, total and loaded properties:

  • the lengthComputable returns a boolean value used to determine the very measurability of the reading progress.
  • the loaded property pinpoints the number of the loaded bytes;
  • getting the total value is another way to find out a File's size.

The readyState property of the FileReader can be used to monitor file reading: it changes from 0 (EMPTY) to 1 (LOADING), and then to 2 (DONE). In addition, the ProgressEvent interface members are employed to bind the process of a read operation to a UI control, especially a <progress> element. Let's assume a file to read has the size of 7748030 bytes. The example below shows a <progress> with its max property set to the file size; the element's value will be changing simultaneously with the bytes transfer:

file selector and progress bar
<input type="file" name="file-picker" accept="*/*" onchange="readFile(this.files[0])">
<progress id="progress-bar" value="0"></progress>
<button onclick="cancelReading()">abort</button>

FileReader instantiation
var fileReader=new FileReader();
var progressBar=document.getElementById("progress-bar");

function readFile(file) {
 progressBar.max = file.size;
 fileReader.onloadstart=loadstartHandler;
 fileReader.onprogress=progressHandler;
 fileReader.onabort=abortHandler;
 fileReader.onerror=errorHandler;
 fileReader.onload=loadHandler;
 fileReader.onloadend=loadendHandler;
 fileReader.readAsText(file);
}

loadstart handler
function loadstartHandler(event) {
 console.log(event.loaded);
 console.log(event.target.readyState);
}

progress handler
function progressHandler(event) {
 progressBar.value=event.loaded;
 console.log(event.loaded);
 console.log(event.target.readyState);
}

dispatching abort event
function cancelReading(event) {
 fileReader.abort();
}

function abortHandler(event) {
 console.warn("Read operation is aborted . . .");
}

error handler
function errorHandler(event) {
 console.error("Error!");
 console.error(event.target.error.code);
}

load handler
function loadHandler(event) {
 progressBar.value=event.loaded;
 console.info("File has been read successfully!");
 console.log(event.target.readyState);
}

loadend handler
function loadendHandler(event) {
 console.info("Reading is over.");
 console.log(event.target.readyState);
}

Output:

event type total loaded readyState
loadstart 7748030 0 1 (LOADING)
progress 7748030 5210112 1 (LOADING)
load 7748030 7748030 2 (DONE)
loadend 7748030 7748030 2 (DONE)