Introduction to the

HTML5 finally gives us a standard way to interact with local files through the File API specification. To illustrate its capabilities, use the File API to create thumbnail previews of images as they are being sent to the server, or to allow the application to save File references while the user is offline. Alternatively, you can use client-side logic to verify that the mimeType of the uploaded content matches its file extension, or to limit the size of the uploaded content.

The specification provides multiple file access interfaces through a “local” file system:

  1. File– Independent file; Provides read-only information such as name, file size, mimeType, and a reference to a file handle.
  2. FileListFileObject class array sequence (consider<input type="file" multiple>Or drag directories or files from the desktop).
  3. Blob– Files can be split into byte ranges.

When combined with the above data structures, the FileReader interface can be used to read files asynchronously through familiar JavaScript event handling. As a result, you can monitor read progress, find errors, and determine when the load is complete. These apis have a lot in common with XMLHttpRequest’s event model.

Note: At the time of writing this tutorial, Both Chrome 6.0 and Firefox 3.6 support the apis necessary to process local files. The file.slice () method has not been supported since Firefox 3.6.3.

Select the file

First you need to check that your browser fully supports the File API:

// Check for the various File API support.
if (window.File && window.FileReader && window.FileList && window.Blob) {
  // Great success! All the File APIs are supported.
} else {
  alert('The File APIs are not fully supported in this browser.');
}
Copy the code

Of course, if your application uses only a few of these apis, modify this code snippet accordingly.

Select using form input

The most straightforward way to load a file is to use the standard element. JavaScript returns a list of selected File objects as FileList. The following example uses the “multiple” attribute to select multiple files simultaneously:

<input type="file" id="files" name="files[]" multiple />
<output id="list"></output>

<script>
  function handleFileSelect(evt) {
    var files = evt.target.files; // FileList object

    // files is a FileList of File objects. List some properties.
    var output = [];
    for (var i = 0, f; f = files[i]; i++) {
      output.push('<li><strong>', escape(f.name), '</strong> (', f.type || 'n/a', ') - ',
                  f.size, ' bytes, last modified: ',
                  f.lastModifiedDate.toLocaleDateString(), '</li>');
    }
    document.getElementById('list').innerHTML = '<ul>' + output.join('') + '</ul>';
  }

  document.getElementById('files').addEventListener('change', handleFileSelect, false);
</script>
Copy the code

Example: Select using form input. Give it a try!

Use drag and drop to select

Another way to load a file is to drag and drop it locally from the desktop to the browser. We can modify the previous example slightly to add drag and drop support.

<div id="drop_zone">Drop files here</div>
<output id="list"></output>

<script>
  function handleFileSelect(evt) {
    evt.stopPropagation();
    evt.preventDefault();

    var files = evt.dataTransfer.files; // FileList object.

    // files is a FileList of File objects. List some properties.
    var output = [];
    for (var i = 0, f; f = files[i]; i++) {
      output.push('<li><strong>', escape(f.name), '</strong> (', f.type || 'n/a', ') - ',
                  f.size, ' bytes, last modified: ',
                  f.lastModifiedDate.toLocaleDateString(), '</li>');
    }
    document.getElementById('list').innerHTML = '<ul>' + output.join('') + '</ul>';
  }

  function handleDragOver(evt) {
    evt.stopPropagation();
    evt.preventDefault();
    evt.dataTransfer.dropEffect = 'copy'; // Explicitly show this is a copy.
  }

  // Setup the dnd listeners.
  var dropZone = document.getElementById('drop_zone');
  dropZone.addEventListener('dragover', handleDragOver, false);
  dropZone.addEventListener('drop', handleFileSelect, false);
</script>Copy the code

Example: Use drag and drop to select. Give it a try!

Note that some browsers treat the element as a local drag and drop target. In the previous example, you tried dragging a file onto an input field.

Read the file

Now for the good part!

When you get the File reference, instantiate the FileReader object to read its contents into memory. After loading, the reader’s onload event is triggered, and its Result property is used to access file data.

FileReader includes four options for reading files asynchronously:

  • FileReader.readAsBinaryString(Blob|File)resultProperty will contain file/ BLOb data in binary string form. Each byte is represented by an integer in the range [0..255].
  • FileReader.readAsText(Blob|File, opt_encoding)resultProperty will contain file/ BLOb data in the form of text strings. The character string is encoded utF-8 by default. Alternative formats can be specified using optional encoding parameters.
  • FileReader.readAsDataURL(Blob|File)resultProperty will contain encoded asData urlFile/BLOb data of.
  • FileReader.readAsArrayBuffer(Blob|File)resultThe property will containArrayBufferFile/BLOb data in object form.

After calling one of these read methods on your FileReader object, you can track its progress using onLoadStart, onProgress, onLoad, onabort, onError, and onLoadEnd.

The following example filters the image from the user-selected content, calls reader.readasdataURL () on the file, and renders the thumbnail by setting the “SRC” attribute to the data url.

<style> .thumb { height: 75px; border: 1px solid #000; margin: 10px 5px 0 0; } </style> <input type="file" id="files" name="files[]" multiple /> <output id="list"></output> <script> function handleFileSelect(evt) { var files = evt.target.files; // FileList object // Loop through the FileList and render image files as thumbnails. for (var i = 0, f; f = files[i]; i++) { // Only process image files. if (! f.type.match('image.*')) { continue; } var reader = new FileReader(); // Closure to capture the file information. reader.onload = (function(theFile) { return function(e) { // Render thumbnail. var span = document.createElement('span'); span.innerHTML = ['<img class="thumb" src="', e.target.result, '" title="', escape(theFile.name), '"/>'].join(''); document.getElementById('list').insertBefore(span, null); }; })(f); // Read in the image file as a data URL. reader.readAsDataURL(f); } } document.getElementById('files').addEventListener('change', handleFileSelect, false); </script>Copy the code

Example: Reading a file. Give it a try!

Try this example with the picture directory!


Split the file

In some cases, reading the entire file into memory may not be the best option. For example, you want to write an asynchronous file uploader. One possible way to increase upload speed is to read and send files in blocks of byte range that are independent of each other. It is then up to the server component to rebuild the files in the correct order.

Fortunately for us, the File interface supports the split method and therefore supports this use case. In this method, the start byte is the first argument, the end byte is the second argument, and the option content-type string is the third argument. The semantics of this method have changed recently, so it has been prefixed by the vendor:

if (file.webkitSlice) {
  var blob = file.webkitSlice(startingByte, endindByte);
} else if (file.mozSlice) {
  var blob = file.mozSlice(startingByte, endindByte);
}
reader.readAsBinaryString(blob);Copy the code

The following example demonstrates how to read a file block. It is worth noting that this example uses onLoadEnd and checks evt.target.readyState instead of the onload event.

<style> #byte_content { margin: 5px 0; max-height: 100px; overflow-y: auto; overflow-x: hidden; } #byte_range { margin-top: 5px; } </style> <input type="file" id="files" name="file" /> Read bytes: <span class="readBytesButtons"> <button data-startbyte="0" data-endbyte="4">1-5</button> <button data-startbyte="5" data-endbyte="14">6-15</button> <button data-startbyte="6" data-endbyte="7">7-8</button> <button>entire file</button> </span> <div id="byte_range"></div> <div id="byte_content"></div> <script> function readBlob(opt_startByte, opt_stopByte) { var files = document.getElementById('files').files; if (! files.length) { alert('Please select a file! '); return; } var file = files[0]; var start = parseInt(opt_startByte) || 0; var stop = parseInt(opt_stopByte) || file.size - 1; var reader = new FileReader(); // If we use onloadend, we need to check the readyState. reader.onloadend = function(evt) { if (evt.target.readyState == FileReader.DONE) { // DONE == 2 document.getElementById('byte_content').textContent = evt.target.result; document.getElementById('byte_range').textContent = ['Read bytes: ', start + 1, ' - ', stop + 1, ' of ', file.size, ' byte file'].join(''); }}; if (file.webkitSlice) { var blob = file.webkitSlice(start, stop + 1); } else if (file.mozSlice) { var blob = file.mozSlice(start, stop + 1); } reader.readAsBinaryString(blob); } document.querySelector('.readBytesButtons').addEventListener('click', function(evt) { if (evt.target.tagName.toLowerCase() == 'button') { var startByte = evt.target.getAttribute('data-startbyte'); var endByte = evt.target.getAttribute('data-endbyte'); readBlob(startByte, endByte); } }, false); </script>Copy the code

Example: Split a file. Give it a try!

Monitor read progress

An added benefit to using asynchronous event processing is the ability to monitor file read progress; This is useful for reading large files, finding errors, and predicting read completion times.

The onLoadStart and onProgress events can be used to monitor read progress.

The following example demonstrates how to monitor read status by displaying a progress bar. To see the progress indicator in action, try reading a large file or a file on a remote drive.

<style> #progress_bar { margin: 10px 0; padding: 3px; border: 1px solid #000; font-size: 14px; clear: both; opacity: 0; -moz-transition: opacity 1s linear; -o-transition: opacity 1s linear; -webkit-transition: opacity 1s linear; } #progress_bar.loading {opacity: 1.0; } #progress_bar .percent { background-color: #99ccff; height: auto; width: 0; } </style> <input type="file" id="files" name="file" /> <button onclick="abortRead();" >Cancel read</button> <div id="progress_bar"><div class="percent">0%</div></div> <script> var reader; var progress = document.querySelector('.percent'); function abortRead() { reader.abort(); } function errorHandler(evt) { switch(evt.target.error.code) { case evt.target.error.NOT_FOUND_ERR: alert('File Not Found! '); break; case evt.target.error.NOT_READABLE_ERR: alert('File is not readable'); break; case evt.target.error.ABORT_ERR: break; // noop default: alert('An error occurred reading this file.'); }; } function updateProgress(evt) { // evt is an ProgressEvent. if (evt.lengthComputable) { var percentLoaded = Math.round((evt.loaded / evt.total) * 100); // Increase the progress bar length. if (percentLoaded < 100) { progress.style.width = percentLoaded + '%'; progress.textContent = percentLoaded + '%'; } } } function handleFileSelect(evt) { // Reset progress indicator on new file selection. progress.style.width = '0%'; progress.textContent = '0%'; reader = new FileReader(); reader.onerror = errorHandler; reader.onprogress = updateProgress; reader.onabort = function(e) { alert('File read cancelled'); }; reader.onloadstart = function(e) { document.getElementById('progress_bar').className = 'loading'; }; reader.onload = function(e) { // Ensure that the progress bar displays 100% at the end. progress.style.width = '100%'; progress.textContent = '100%'; setTimeout("document.getElementById('progress_bar').className='';" , 2000); } // Read in the image file as a binary string. reader.readAsBinaryString(evt.target.files[0]); } document.getElementById('files').addEventListener('change', handleFileSelect, false); </script>Copy the code

Example: Monitor the read progress. Give it a try!

Tip: To see progress indicators in action, try reading resources in large files or remote drives.

reference

  • The File API specification
  • FileReader interface specification
  • Blob interface specification
  • FileError interface specification
  • The ProgressEvent interface specification