Friday, April 8, 2016

The File API

The File API

INTRODUCTION

The objective of this chapter is to provide an overview of the File API.
sound sample editor serverlessBefore HTML5, file management was limited to multipart forms and to Ajax for sending/requesting files to/from a remote Web server.
Possible actions were limited, both for the developer and for the user. However, HTML5 now comes with an API called "File"  that holds features for accessing file metadata (name, size, type) from client-side JavaScript. The API has also methods for reading file contents directly in the browser. This is particularly interesting for displaying preview of images before uploading them, or - and this is much more interesting - for developing Web applications that work with local files without the need for a server. Imagine a multimedia player that accesses (in read-only) your file system, read your audio and video files, etc., such as the Remo Audio player below (Google Chrome extension written in HTML5), or an application that edits the audio content of local mp3 files, for example, such as the HYA-WAVE sound editor (screenshot above).
audio player that plays local files
polarr photo editor uses the File API

Getting details about a file: reading metadata

Imagine you have an input field like this:
  1. Select one or more files: <input type="file" id="input"/>
Treading file metadatahis will render as a "select files" or "browse files" button. If you select one file in the file chooser dialog that has popped up, before HTML5 you couldn't do anything with it in the client-side: no access from JavaScript. With the File API, you can read what we call "file metadata": name, size, type and last modification date.
Look at the the code below: the file API defines a files property on the DOM node corresponding to the <input type="file".../> input field. This property is an array.
In the example below, we get in the selectedFile variable the metadata related to the first selected file:
  1. var selectedFile = document.getElementById('input').files[0];
  2. // do something with selectedFile.name, selectedFile.size, selectedFile.type
  3. // selectedFile.lastModifiedDate
  4. ...

EXAMPLE 1: READ METADATA OF THE FIRST SELECTED FILE

Here is a complete example on JS Bin that uses the code above to get details about the first selected file. Please try it below on your browser (click on the button and choose one file):
Select one or more files: 
  • File name:
  • File size:
  • File type:
  • File last modification date:
Complete source code:
  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset=utf-8 />
  5. <title>Reading file metadata</title>
  6. <script>
  7.      function displayFirstSelectedFileMetadata() {
  8.         var selectedFile = document.getElementById('input').files[0];
  9.         document.querySelector("#singleName").innerHTML = selectedFile.name;
  10.         document.querySelector("#singleSize").innerHTML = selectedFile.size +" bytes";
  11.         document.querySelector("#singleType").innerHTML = selectedFile.type;
  12.         document.querySelector("#singleDate").innerHTMLselectedFile.lastModifiedDate;
  13. }
  14. </script>
  15. </head>
  16. <body>
  17.    Select one or more files: <input type="file" id="input"
  18.                                    onchange="displayFirstSelectedFileMetadata();"/>
  19. <p>
  20. <ul>
  21.     <li>File name: <span id="singleName"></span></li>
  22.     <li>File size: <span id="singleSize"></span></li>
  23.     <li>File type: <span id="singleType"></span></li>
  24.     <li>File last modification date: <span id="singleDate"></span></li>
  25. </ul>
  26. </body>
  27. </html>

EXAMPLE 2: DISPLAY METADATA OF MULTIPLE FILES, USE A FILTER ON THE FILE TYPE

This example is a bit more complicated, as it will display details about all files selected (not only the first) and allows only images to be selected, using the accept attribute of the input field: <input type="file" accept="image/*".../>.
Example on JS Bin, or try it in your browser: click on the button, and select multiple image files. Notice that in the file selector, files that are not images will be greyed and non selectable.
Select several images: 
...
Source code extract:
  1. Select several images: <input type="file" accept="image/*" multipleonchange="filesProcess(this.files)" name="selection"/>
  2. <p>
  3. <div id="result">...</div>
  4. <script>
  5.   function filesProcess(files) {
  6.       var selection = "<table><tr><th>Name</th><th>Bytes</th><th>MIME Type</th>
  7.                        <th>Last modified date</th></tr>";
  8.       for(i=0; i<files.length ;i++){
  9.           file = files[i];
  10.           selection += "<tr><td>"+file.name+"</td><td style=\"text-align:right\">"
  11.                     +file.size+"</td><td>"
  12.                     +file.type+"</td><td> "+file.lastModifiedDate+"</td></tr>";
  13.       }
  14.       selection += "</table>";
  15.  
  16.       document.getElementById("result").innerHTML = selection;
  17.   }
  18. </script>
Explanations:
    • Line1: we used the multiple attribute to allow the selection of multiple files in the file chooser (using shift or control keys). The accept="image/*"  attribute is a filter that makes selection possible only for images. Finally, the onchange listener will call the fileProcess(...) function, passing as parameter the list of selected files for the current element (this.files). 
    • Lines 7 and 12: we prepare the HTML code for building a <table> with the results.
    • Line 10: this for loop builds all the rows that compose the table, adding HTML code to the selectionstring variable. At the end of the loop, this variable contains all the HTML code that corresponds to the table of results.
    • Line 18: the table is added to the page. We use the innerHTML attribute of the DOM element corresponding to the <div id="result"> in order to insert the table as its child in the DOM tree. As such, the table appears on the page dynamically.

Blob and File, what is that?

THE HTML5 FILE API SPECIFICATION INTRODUCES SEVERAL NEW INTERFACES

  • the blob movie poster
      • The FileList interface (we already met it: the files property is aFileList),
      • the File interface (the file variable in the for loop of the last example is of that type) that is useful for getting details about a file,
      • the Blob interface helps read binary data (only) that is accessed slice by slice (as chunks of data, each one is a "Blob"),
      • and a FileReader interface for reading file content (we will see how to use it in the next section of the course),
    We will not use all of these new interfaces, but let's explain the difference between Blob and File, as most of the methods exposed by theFileReader interface take indifferently a Blob or a File as parameter.

THE BLOB OBJECT

  • An object of type Blob is a structure that represents binary data available as read-only. Most of the time, you will encounter these objects only when you handle files.
    Blob objects have two properties named size and type which respectively retrieve the size in bytes of the data handled by the Blob and their MIME type.
    There is also a method called slice(), but this is not used in common applications. If you are curious, check the "slicing a file" section of this "Reading files in JavaScript using the File APIs" article.

THE FILE OBJECT

  • funny pict about colored files
    File objects are useful for manipulating... files! They inherit the properties and methods of Blob objects, and have two additional properties that are name, for the file name, and lastModifiedDate to get the date of the last modification of the file (in the form of a JavaScript Date object, obviously) .
    Most of the time, we will work with File objects. Blobobjects will have real interest when the Filesystem API is widely available (at the moment there is only an experimental version in Chrome), or when you download binary files using Ajax (see example below). This last API will be covered in the HTML5 part-2 course.
    [Advanced] Just for those of you that would like to see how Blob objects can be used, here is an example "as is" that shows how to download an image using Xhr2 (Xml Http Request version 2), the examples uses a<progress> element to show the download progress, and uses xhr.responseType = 'blob'; to indicate that the file we are going to download is a binary file (a blob). Try the example, then comment the line withresponseType='blob'. In that case, you will notice that the image file is not properly decoded by the browser and is not displayed in the page. Xhr2 will be covered in the HTML5 part-2 course.

Reading file content

INTRODUCTION / TYPICAL USE

funny picture

Step1: create a FileReader object

The file API proposes several methods for reading a file content, each taken from theFileReader interface. Here is how you create a FileReader object:
  1. var reader = new FileReader();

Steps 2 & 3: first call a method of the FileReader object for reading the file content, then get the file content in an onload callback

There are three different methods available for reading a file content: readAsTextreadAsArrayBuffer for binary data and also as readAsDataURL (the content will be a URL you will use to set the src field of an <img src=...>, <audio>, <video>, and also with all existing methods/properties that accept a URL).
All these methods take as a unique parameter a File object (for example, a file chosen by a user after clicking on a <input type=file> input field). Below, we use, as an example, the readAsText method:
  1. function readFileContent(f) {
  2.    // Executed last: called only when the file content is loaded, e.target.result is
  3.    // The content
  4.    reader.onload = function(e) {
  5.        var content = e.target.result;
  6.        // do something with the file content
  7.        console.log("File " + f.name + " content is: " + content);
  8.    };
  9.    // Executed first: start reading asynchronously the file, will call the
  10.    // reader.onload callback only when the file is read entirely
  11.    reader.readAsText(f);
  12. }
The above code shows how a file can be read as text. The function is called, for example by clicking on the button corresponding to a <input type="file" id="file"  onchange="readFileContent(this.files)"/>, and by choosing a file.
    • Line 12 is executed first, and asks the Reader object to read the file f as text. As this takes some time, it's an asynchronous operation that will be executed by the browser in background. When the file is read, then the reader.onload callback function is called.
    • Line 4 is executed after line 12, and is called only when the file content is available. This callback takes an event e as a unique parameter, and e.target.result is the file content.
Try a variation of the the above code in your browser, that displays the file content in a text area. This example is detailed further in the course. Click and select a text file below:



In the next course sections, we will look at different examples that read file contents as text, dataURL and binary.

Practical examples: reading file content as text

EXAMPLE 1: READ A SINGLE FILE CONTENT

Example at JS Bin, or try it here in your browser
Choose a text file :
Complete source code:
  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="utf-8">
  5. <title>Example of use of FileReader with a text file</title>
  6. </head>
  7. <body>
  8. <label for="files">Choose a text file:</label><input type="file" id="file"
  9.                            onchange="readFileContent(this.files)"/><br/>
  10. <p>
  11. <textarea rows=15 cols=50 id="fileContent"></textarea>
  12. <script>
  13. function readFileContent(files) {
  14.      console.log("In readFileContent");
  15.      var reader = new FileReader();
  16.     // Executed last: called when the file content is loaded, e.target.resultis
  17.     // The content
  18.     reader.onload = function(e) {
  19.         // display content in the textarea with id="fileContent"
  20.         document.getElementById("fileContent").value= e.target.result;
  21.     };
  22.     // Read in the tfile as text
  23.     console.log("Reading file:" + files[0].name);
  24.     // Executed first: start reading asynchronously the file, will call the onload
  25.     // callback when the file is read
  26.     reader.readAsText(files[0]);
  27. }
  28. </script>
  29. </body>
  30. </html>
This example is the one at the end of the previous page. This time, we show the complete source code above. Remember that the instruction at line 30 is executed first, then when the file is read, the browser will call asynchronously the onload callback at line 20.

EXAMPLE 2: A VARIATION OF THE PREVIOUS ONE, USING MULTIPLE FILES

Example on JS Bin, or try it below in your browser. This time, please select multiple text files (using shift for multiple selection):

Source code:
  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="utf-8">
  5. <title>Example of use of FileReader with a text file</title>
  6. </head>
  7. <body>
  8. <label for="files">Choose multiple text files:</label>
  9. <input type="file" id="files"
  10.        multiple onchange="readFilesAndDisplayAsText(this.files);"/><br/>
  11. <p>
  12. <textarea rows=30 cols=50 id="filesContent"></textarea>
  13. <script>
  14. var filesContent = document.getElementById("filesContent");
  15. function readFilesAndDisplayAsText(files) {
  16.      console.log("dans read files");
  17.      // Loop through the FileList
  18.      for (var i = 0, f; f = files[i]; i++) {
  19.          var reader = new FileReader();
  20.          // Add an onload listener to the reader
  21.          addOnLoadListener(reader, f.name);
  22.          // start reading, will call the listener later, when the file f is read
  23.          reader.readAsText(f);
  24.      }
  25. }
  26. function addOnLoadListener(reader, name) {
  27.      // Add an onload listener that will be able to print the name of the
  28.      // file...
  29.      reader.onload = function(e) {
  30.          filesContent.value += "###### READING FILE " + name + " ######";
  31.          filesContent.value += e.target.result;
  32.      };
  33. }
  34. </script>
  35. </body>
  36. </html>
Explanations:
This example is similar to the previous one except that this time we read multiple files.
  • Line 20: this is the for loop that will iterate on the files object passed as parameter by the onchangelistener declaration at line 10
  • Line 25: instead of declaring the onload listener with a reader.onload =... directly in the loop, we preferred this time to write a separate function that will do that. This technique is useful when you want the listener to work with extra variables computed in the loop (in our case the name of the file).

SOME COMPLEMENTS ABOUT ENCODING

Note that you can optionally indicate the encoding of the file you are going to read (default is UTF-8):
  1. reader.readAsText(file, 'UTF-8');
  2. reader.readAsText(file, 'ISO-8859-1');
  3. ..

Read file content as binary

INTRODUCTION

This method is rarely used except for loading "raw" binary data. For images you would like to see in your HTML page using the <img src= tag> or for drawing in a canvas, or for audio and video files that you would like to play using the <audio> or <video> elements, you would preferably use the readAsDataURL method presented in the next page of the course.
readAsArrayBuffer is often used for reading audio samples that should be loaded in memory and played using the WebAudio API, for loading textures that you will use width WebGL for 3D animations, etc.

EXAMPLE 1: READ A LOCAL AUDIO FILE AND PLAY IT WITH THE WEBAUDIO API

The WebAudio API is useful for reading audio sound sample from memory (no streaming), and has been designed for music application and games. This example shows how a local audio file can be read and played directly in the browser, without the need for a server!
Example on JS Bin (does not work on IE, as it does not support the WebAudio API). We could not embed it here on the edX platform as it prevents code that uses Ajax to run in its pages.
local audio player
Source code extract:
  1. // User selects file, read it as an ArrayBuffer and pass to the API.
  2. var fileInput = document.querySelector('input[type="file"]');
  3. fileInput.addEventListener('change', function(e) {
  4.    var reader = new FileReader();
  5.    reader.onload = function(e) {
  6.       initSound(e.target.result);
  7.    };
  8.    // THIS IS THE INTERESTING PART!
  9.    reader.readAsArrayBuffer(this.files[0]);
  10. }, false);
Explanations:
    • Line 1we get a pointer to the file selector, the variable fileInput.
    • Line 4we define a change listener. In this example, we use an anonymous function directly included in the listener definition (the listener is the function(e) {...}).
    • Line 11when a user chooses a file, the listener will be executed. Line 11 will start the reading of the file content, as a binary file (this is what readAsArrayBuffer means: read as binary!). Once the file will be entirely read, the onload callback will be asynchronously called by the browser.
    • Line 7 is the onload callback, executed when the file content is loaded in memory. We pass the file content to the initSound function (see JS Bin example for complete source code) that uses WebAudio to decode it (it may be a compressed file, a mp3 for example, and WebAudio works only with uncompressed audio formats in memory), and to play it.

Read file content as data URL

INTRODUCTION TO A DATA URL

  • funny picture of  a machine for building data URLs
    What is a data URL?
    A data URL is a URL that includes type and content at the same time. It is useful, for example,  for inlining images or videos in the HTML of a Web page (on mobile devices, this may speed up the loading of the page by reducing the number of HTTP requests).
    Here is an example of a red square, as a data URL. Copy and paste it in the address bar of your browser, and you should see the red square:
    1. data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==
    This data URL in a browser address bar should look like that:
    data url in adress bar shows a red circle
    If we set the src attribute of an image element <img src="data:image/png...."> with the data URL of the above screenshot, it will work. Exactly as if you used a URL that started with http://
    In your browser, you will see a small red circle rendered by this source code:
    1. <img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUA
    2. AAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO
    3. 9TXL0Y4OHwAAAABJRU5ErkJggg==" alt="Red square" width=50 height=50/>
    And here is the result:
    Red square
    This dataURL format enables storing a file content in a base64 format (as a string), and adds the MIME type specification of the content. The dataURL can therefore store a file as a URL readable with modern browsers. Its use is becoming more common on the Web, especially for mobile applications, as inlining images reduces the number of HTTP requests and makes Web page load faster.
    You will find lots of Web sites and tools for generating dataURL from files, such as the DataURL maker Web site (screenshot below):
    dataURL maker web site
    With the above example, you can copy and paste the characters on the left and use them with an <img src="...">. Just set the src attribute with it!
    Notice that you can encode as dataURL any type of file, but the most frequent usage comes with media files (images, audio, video).

EXAMPLE 1: READ IMAGES AS DATA URL AND DISPLAY PREVIEWS IN THE PAGE

  • Example 1 is useful for forms that allow the user to select one or more pictures. Before sending the form, you might want to get a preview of the pictures in the HTML page. The reader.readAsDataUrl method is used for that.
    Example on JS Bin or try it below in your browser:
     
    Preview of selected images:
    Source code extract:
    1. <label for="files">Choose multiple files:</label>
    2. <input type="file" id="files" multiple
    3.         onchange="readFilesAndDisplayPreview(this.files);"/><br/>
    4. <p>Preview of selected images:</p>
    5. <output id="list"></output>
    6.  
    7. <script>
    8.   function readFilesAndDisplayPreview(files) {
    9.     // Loop through the FileList and render image files as thumbnails.
    10.     for (var i = 0, f; f = files[i]; i++) {
    11.  
    12.     // Only process image files.
    13.     if (!f.type.match('image.*')) {
    14.         continue;
    15.     }
    16.  
    17.     var reader = new FileReader();
    18.  
    19.     //capture the file information.
    20.     reader.onload = function(e) {
    21.         // Render thumbnail. e.target.result = the image content
    22.         // as a data URL
    23.        // create a span with CSS class="thumb", for nicer layout
    24.        var span = document.createElement('span');
    25.        // Add an img src=... in the span, with src= the dataURL of
    26.        // the image
    27.        span.innerHTML = "<img class='thumb' src='" +
    28.                          e.target.result + "' alt='a picture'/>";
    29.        // Insert the span in the output id=list
    30.        document.getElementById('list').insertBefore(span, null);
    31.    };
    32.   // Read in the image file as a data URL.
    33.   reader.readAsDataURL(f);
    34.  }
    35. }
    Explanations:
      • Line 35: starts the reading of the file f. When f is read the onload callback will be called.
      • Lines 25-31: we build, using the DOM API, a <span class="thumb">...</span> and inside we add an<img src=the data url> element with its src attribute equal to the url of the image that has been read (the image content as dataURL is in e.target.result). Finally, at line 31 we insert the span in the document before the current children of the <output id="list"> element (declared at line 5).

EXAMPLE 2: READ A SINGLE LOCAL IMAGE FILE AND USE IT WITH DRAWIMAGE IN A CANVAS

  • read image as dataURL and draw inside a canvas. Jsbin screenshot
    Errata: the above screenshot says "choose multiple files", but the example works only with a single file.
    Source code extract:
    1. function drawImage(imageFile) {
    2.    var reader = new FileReader();
    3.  
    4.    //capture the file information.
    5.    reader.onload = function(e) {
    6.       // For drawing an image on a canvas we
    7.       // need an image object
    8.       var img = new Image();
    9.       // Even if the file has been read, decoding
    10.       // the dataURL format may take some time
    11.       // so we need to use the regular way of
    12.       // working with images: onload callback    
    13.       // that will be called after setting the src attribute
    14.       img.onload = function(e) {
    15.          // draw the image!
    16.          ctx.drawImage(img, 0, 0, 400, 400);
    17.       }
    18.       // e.target.result is the dataURL, so we set the
    19.       // src if the image with it. This will call
    20.       // asynchonously the onload callback
    21.       img.src= e.target.result;
    22.   };
    23.   // Read in the image file as a data URL.
    24.   reader.readAsDataURL(imageFile);
    25. }
    26. function readFileAndDraw(files) {
    27.     drawImage(files[0]);
    28. }
    Explanations:
    Remember how we worked with images on a canvas. We had to create an empty image object (line 8), set thesrc attribute of the image object (line 23), then use an image.onload callback (line 15), and we could only draw from inside the callback (line 17). This time, it's exactly the same, except that the URL comes frome.target.result in the reader.onload callback (line 23).

EXAMPLE 3 (ADVANCED): AN INSTAGRAM-LIKE PHOTO FILTER APPLICATION

  • Another very impressive example, has been developed by @GeorgianaB, a student of the first iteration of this course. This Web applications reads local image files, draws them into a canvas element and proposes different filters. This example is given "as is" for those of you who would like to go further. Just click on the link (or on the image below) and look at the source code.
    Try this example online on gitHub (or click the screenshot below)
    instagram like filters

EXTERNAL RESOURCES

1 comment:

  1. Hi, Great.. Tutorial is just awesome..It is really helpful for a newbie like me.. I am a regular follower of your blog. Really very informative post you shared here. Kindly keep blogging. If anyone wants to become a Front end developer learn from Javascript Online Training from India . or learn thru JavaScript Online Training from India.

    ReplyDelete