The File API in JavaScript: How to read, validate, and manipulate files in the browser

- 👤 Andrés Cruz

🇪🇸 En español

The File API in JavaScript: How to read, validate, and manipulate files in the browser

The JavaScript File API has become a key piece for many modern web applications. In my experience developing applications that handle high volumes of files, being able to inspect and validate data before sending it to the server makes a huge difference in both performance and user experience.

A few years ago, these types of validations could only be done on the backend side. Today, thanks to the HTML5 File API, much of that work can be resolved directly in the browser, saving time, resources, and unnecessary errors.

In this article, I am going to explain what the JavaScript File API is, how it works, and, above all, how to use it practically to read, validate, preview, and even generate files without going through the server.

What is the JavaScript File API and what is it used for?

The File API is a set of interfaces introduced with HTML5 that allow web applications to access local files selected by the user. This API does not give free access to the file system (for security reasons), but only to the files that the user decides to upload.

With it, it is possible to:

  • Get file metadata (name, size, MIME type).
  • Read the content of text files, images, audio, etc.
  • Preview files before uploading them.
  • Validate files without sending them to the server.
  • Create downloadable files from JavaScript.

Advantages of processing files on the client side

When I have worked on forms where users upload dozens of files, validating from the browser avoids uploading incorrect files that are too large or have an invalid format. This translates into:

  • Lower server load.
  • Immediate response to the user.
  • Better user experience.
  • Fewer errors in subsequent processes.

File API compatibility with browsers

The File API is supported by all modern browsers, but it remains good practice to check for its availability before using it.

How to check for File, FileList, and FileReader support

A simple validation is to check for the existence of the main interfaces.

As it is a technology provided since the HTML5 API, it may undergo changes from one browser version to another; therefore, it is necessary to validate that the browser actually has the corresponding support for this API.

To verify compatibility, we can use the following JavaScript code:

if (!(window.File && window.FileList && window.FileReader)) {
 console.log('La API de archivos no está soportada en este navegador');
 return;
}

This type of check is something I usually add at the beginning, especially if the application must function in controlled environments or old browsers.

File properties in JavaScript

Once the user selects a file, JavaScript represents it using a File object. This object exposes several read-only properties that are very useful for validations.

Name, size, MIME type, and modification date

The most important properties are:

  • file.name → file name.
  • file.size → size in bytes.
  • file.type → MIME type (image/png, text/plain, etc.).
  • file.lastModified → timestamp of the last modification.

With just these properties, you can, for example, prevent files larger than a certain size or with unpermitted extensions from being uploaded, which in practice avoids many problems.

The Filelist object is referenced although it is not used directly by this name; it allows returning a list of selected files (for example) in an input type="file".

File selection with input type="file"

The most common way to work with the File API is through an HTML field:

<input type="file" id="files" multiple>

Multiple file selection

The multiple attribute allows selecting several files at once. This is especially useful in bulk upload applications.

const input = document.getElementById('files');

input.addEventListener('change', function () {
  const files = this.files;

  for (let i = 0; i < files.length; i++) {
    const file = files[i];
    console.log(
      file.name,
      file.type || 'n/a',
      file.size,
      file.lastModified
    );
  }

Get and display file information

const input = document.getElementById('files');
input.addEventListener('change', function () {
 const files = this.files;
 for (let i = 0; i < files.length; i++) {
   const file = files[i];
   console.log(
     file.name,
     file.type || 'n/a',
     file.size,
     file.lastModified
   );
 }
});

In more than one project, I have used this approach to show the user a clear summary of what they are about to upload before confirming the operation.

File data

It is essential for web applications that handle high volumes of files during user upload to be able to validate or verify the data that makes up the file. Previously, we had to upload the file so that these validations could be performed on the server side; now it is also possible to perform part of these validations on the client side with the JavaScript File API.

So far, we have discussed some JavaScript APIs that allow multiple functions that would be very difficult to do without them; now we will see how to work with part of the JavaScript File API; we will see how to obtain data from files that are loaded locally through input type="file" and derivatives.

Getting access to user files

The most common method to load files into an application is by using the input type="file" tag; once loaded with the help of the File API, we can obtain a list of files loaded by the user and their information.

The File API 100% supports the multiple attribute present in the input type="file" tag.

Showing data from files loaded in an input type="file" multiple

The following example allows showing the different information obtainable through the File API for files selected by an input type="file" multiple:

if (!(window.FileList)) {
    console.log('La API FileList no está soportada');
    return;
}
var files = document.getElementById('files').files;
var output = [];
for (var i = 0, f; f = files[i]; i++) {
    output.push('<li>', escape(f.name), '(', f.type || 'n/a', ') - ',
            f.size, ' bytes, last modified: ',
            f.lastModifiedDate.toLocaleDateString(), '</li>');
}
document.getElementById('list').innerHTML = '<ul>' + output.join('') + '</ul>';

View example Download

Showing data from files loaded via Drag and Drop

The following example is a variation of the previous one, and we can use Drag and Drop for file loading and subsequently display the file information:

    function drop(event) {
        event.stopPropagation();
        event.preventDefault();
        if (!(window.FileList)) {
            console.log('La API FileList no está soportada');
            return;
        }
        var files = event.dataTransfer.files;
        var output = [];
        for (var i = 0, f; f = files[i]; i++) {
            output.push('<li>', escape(f.name), '(', f.type || 'n/a', ') - ',
                    f.size, ' bytes, última modificación: ',
                    f.lastModifiedDate.toLocaleDateString(), '</li>');
        }
        document.getElementById('list').innerHTML = '<ul>' + output.join('') + '</ul>';
    }
    function dragenter(event) {
        event.stopPropagation();
        event.preventDefault();
    }
    function dragover(event) {
        event.stopPropagation();
        event.preventDefault();
    }
    var contenedor = document.getElementById("contenedor");
    contenedor.addEventListener("dragenter", dragenter, false);
    contenedor.addEventListener("dragover", dragover, false);
    contenedor.addEventListener("drop", drop, false);

View example Download

The simplest example:

function dragover(e) {
  e.preventDefault();
}

function drop(e) {
  e.preventDefault();
  const files = e.dataTransfer.files;
  handleFiles(files);
}

const dropzone = document.getElementById('dropzone');
dropzone.addEventListener('dragover', dragover);
dropzone.addEventListener('drop', drop);

Reading file content with FileReader

Being able to access the content of files loaded on a web page, or in other words, allowing the reading of files loaded into the web application through a native JavaScript API, opens up a huge number of possibilities for creating web applications for file handling and editing.

Plain text files can be loaded for editing the text that makes them up, content can be validated, previews can be shown, etc., and all this from the client side without the need to connect to another server.

Previously, we explained the different properties that exist in the JavaScript File API to obtain file data; now we will go a bit further to reach their opening for a subsequent reading of files loaded via input type="file" or other methods like Drag and Drop.

To read the content of a file, the FileReader interface is used, which works asynchronously.

readAsText vs readAsDataURL vs readAsArrayBuffer

The most used methods are:

  • readAsText: ideal for .txt, .csv, .json files.
  • readAsDataURL: perfect for images and previews.
  • readAsArrayBuffer: useful for working with binary data.

Choosing the right method is important. In real applications, using the incorrect method can unnecessarily complicate processing.

FileReader events and error control

The most common event is onload, which triggers when the file has been read correctly.

const reader = new FileReader();
reader.onload = function (e) {
  console.log(e.target.result);
};

1.0 Verifying browser compatibility

First of all, and like any new technology, it is recommended to verify that the browser has support to use the API; reference should be made to the File interface instead of the FileList interface, which provides read-only data from the file such as its name and, more importantly, its content.

To verify compatibility, we can use the following JavaScript code:

if (!(window.File)) { console.log('The File API is not supported');    return; }

Once the support of the File API (window.File) by the browser is guaranteed, it is then possible to use the different methods and properties that the File API has for reading it.

2.0 Obtaining files from the user

The most common method to obtain files from the user is through the input type="file" tag; although any API that returns the list of files and their content will be accessed with the File API, like the Drag and Drop experiment we saw earlier.

It is possible to obtain multiple pieces of information about the list of files that were currently loaded (supports the multiple attribute).

3.0 the FileReader interface for reading files

This interface provides us with methods for reading files such as:

  • FileReader.readAsBinaryString(Blob|File): The file or blob must be specified; the result property will contain the data of the file/BLOB object in the form of a binary string. Each byte is represented by an integer between 0 and 255, both inclusive.
  • FileReader.readAsText(Blob|File, [opt_encoding]): The file or blob and optionally the encoding (which defaults to UTF-8) must be specified; the file must be plain text.
  • FileReader.readAsDataURL(Blob|File): The file or blob must be specified, and the content must be base64 encoded.

All these methods have in common the passing of the file as parameters to be able to access its content; once its content is accessed and read, the loadend event is triggered.

Although for the presented examples (which are assumed to be plain text), using any of the methods seen previously would work for us, even if some return the MIME among other things.

If you want more information, do not hesitate to consult the MDN: FileReader.

3.1 FileReader interface events

The loadend event triggers after the file reading is successful, and from here the content of the file is accessed to do something with it.

Reading files captured via an input type="file"

The most basic or common use that comes to mind is capturing the file selected by an input type="file" and showing its content:

            var MAX_BYTES = 102400; // 100 KB
            function filesInfo(event) {
                if (!(window.File)) {
                    console.log('La API File no está soportada');
                    return;
                }
                var file;
                var reader;
                var files = document.getElementById('files').files;
                for (var i = 0; i < files.length; i++) {
                    file = files[i];
                    reader = new FileReader();
                    reader.onloadend = onFileLoaded;
                    reader.readAsBinaryString(file);
                }
            }
            function onFileLoaded(event) {
                document.getElementById("resultado").innerHTML = event.currentTarget.result.substr(0, MAX_BYTES);
            }

We see that the structure is similar to those seen in the previous entry: except that now we use the FileReader method for reading the files and the subsequent call of the onloadend event upon finishing the reading; finally, the experiment:

View example Download

Reading files from a file input more simply:

document.getElementById('files').addEventListener('change', function () {
 const file = this.files[0];
 const reader = new FileReader();
 reader.onload = function (e) {
   console.log(e.target.result);
 };
 reader.readAsText(file);
});

This pattern is one of the most common and, in my experience, sufficient for most text validations and previews.

Reading files captured via Drag and Drop

The last example consists of adapting the experiment seen previously with Drag and Drop, using all the Drag and Drop events until the capture of the file in the drop() event:

     var MAX_BYTES = 102400; // 100 KB
                function dragenter(event) {
                    event.stopPropagation();
                    event.preventDefault();
                }
                function dragover(event) {
                    event.stopPropagation();
                    event.preventDefault();
                }
                function drop(event) {
                    console.log('drop', event);
                    event.stopPropagation();
                    event.preventDefault();
                    var data = event.dataTransfer;
                    var files = data.files;
                    var file;
                    var reader;
                    for (var i = 0; i < files.length; i++) {
                        file = files[i];
                        reader = new FileReader();
                        reader.onloadend = onFileLoaded;
                        reader.readAsText(file);
                    }
                }
                function onFileLoaded(event) {
                    document.getElementById("resultado").value = event.currentTarget.result.substr(0, MAX_BYTES);
                }
                var contenedor = document.getElementById("contenedor");
                contenedor.addEventListener("dragenter", dragenter, false);
                contenedor.addEventListener("dragover", dragover, false);
                contenedor.addEventListener("drop", drop, false);

View example Download

Reading files using Drag and Drop more simply:

function handleFiles(files) {
 for (let i = 0; i < files.length; i++) {
   const reader = new FileReader();
   reader.onload = function (e) {
     console.log(e.target.result);
   };
   reader.readAsText(files[i]);
 }
}

Creating a downloadable file with JavaScript

View example Download source

Generally, when you want to download a file from a web application, a request must be made to the server for the file in question so that it can then be downloaded through the user's browser; with a few lines of code and using data URIs, it is possible to create files to be downloaded without the need for these types of requests; the JavaScript code we will see below allows generating a plain text file (TXT) from the content of a text field; specifically from a textarea so that it can then be downloaded; first we will see how to assemble a data URI:

Data URI syntax

Data URIs allow embedding content into small files; taking an excerpt from the official documentation offered by MDN on data URIs, they follow the following syntax:

data:[<mediatype>][;base64],<data>
<mediatype>

Allows indicating the file type such as: 

  • Text: Textual data (readable) such as text/plain (default) or text/html.
  • Image: Binary data to represent images: image/jpeg, image/gif, image/png.
  • Audio: Digital sound data: audio/basic, audio/wav video.
  • Video: Video data: video/mpeg.
;base64 (optional)If it is plain or textual data, the text is simply embedded; otherwise, ;base64 must be specified to embed binary data such as images or videos.

Next, the file data is indicated; example:

data:,Hello%2C%20World!

Corresponds to a plain text file.

Script to generate and download files

<textarea id="txt"></textarea>
<a href="#" id="link" download="contenido.txt">Descargar el contenido del textarea</a>
    window.onload = function() {
      var txt = document.getElementById('txt');
      txt.value = window.onload + '';
	   document.getElementById('link').onclick = function(code) {
        this.href = 'data:text/plain;charset=utf-8,'
          + encodeURIComponent(txt.value);
      };
    };

How does the script to generate and download files work?

The heart of the script:

     document.getElementById('link').onclick = function(code) {
        this.href = 'data:text/plain;charset=utf-8,'
          + encodeURIComponent(txt.value);
      };

The encodeURIComponent() function encodes the text string and embeds it within the href attribute of the link for download through the link's download attribute when a click is detected.

View example Download source

Real-world use cases of the File API in web applications

Some very common uses in real projects:

  • Validating file size and type before uploading.
  • Showing image previews.
  • Reading CSV files to import data.
  • Editing text files directly in the browser.
  • Generating downloadable files on the fly.

In all these scenarios, the File API allows for creating much faster and friendlier experiences.

Frequently Asked Questions about the JavaScript File API

  • Can JavaScript read any file from the system?
    • No. It can only access files that the user explicitly selects.
  • Is the File API secure?
    • Yes. Browsers impose very strict restrictions to protect the user.
  • Can I read large files?
    • Yes, but it is advisable to validate the size and limit reading to avoid performance issues.
  • Does it work in all browsers?
    • It works in all modern browsers.

Conclusions

The JavaScript File API opens a huge range of possibilities for handling files directly in the browser. From simple validations to advanced reading and file generation, this API allows for reducing server dependencies and significantly improving the user experience.

In applications with many files, processing part of the work on the client side is not just an improvement: it is almost a necessity.

I agree to receive announcements of interest about this Blog.

Learn what the File API is in JavaScript, how to read, validate, and manipulate local files with practical examples using File, FileReader, and Drag and Drop.

| 👤 Andrés Cruz

🇪🇸 En español