How to read and save files in JavaScript starting with Chrome 86

A new API for new types of web applications

As of this writing Chrome 86 is about to release in a few weeks and it brings an exciting new feature: The Native File System API. This API allows you to build web pages that read and/or save files to the file system with JavaScript in the browser. Until now you only could read files with an HTML input and saving was not possible with browsers. You can now provide a improved user experience if your application deals with files in the web browser.

Be careful this is a new API that is not implemented in all browsers, yet. You can check current browser support here. There is a W3C Draft but it’s not in the Standards track. Google has been working on this with several experiments in Chrome and the API might change. Right now you can offer these features only to Google Chrome users and you need to make sure the methods exist.

This article on web.dev explains a lot but it is not entirely up to date as I am writing this. An update to this post will come soon. Many other sites use the old syntax, too. This blog post is supposed to give a short introduction and code examples with the syntax that is shipping in Chrome 86 in October 2020.

Reading files from a directory

My first use case was that I have a directory of files on my computer that I want to load into my web app for processing, uploading etc. So using the post from web.dev and updating it to the latest method names for Chrome 86 I came up with this little function:

async openDirectory() {
    const handle = await window.showDirectoryPicker();
    const files = [];
    for await (const entry of handle.values()) {
      const file = await entry.getFile();
      files.push(file);
    }
    return files;
}

My web app works with the standard JavaScript File interface. The array files is now an array of File objects and I can use them just like the old implementation. The old way used an HTML <input type="file" /> to let the user select files and load them into my app. Now we use the showDirectoryPicker method to allow the user to pick the director with the files. If you need to pick only one file the method is called showOpenFilePicker.

Now we have the modern version of my implementation with the change that the user will now get a permission prompt to confirm that the browser can read from the selected directory. If you need more information about the security and permission stuff please check the previously mentioned web.dev post and W3C spec. The W3C spec even has good examples for web developers.

Writing files to a directory

The more important use case is writing files to disk/file system. This was previously not really possible. You could probably find workarounds like downloads but we now have a nice API to let users choose a directory and save files there. This is the method I came up with after updating the example code:

async exportToFiles() {
    const directoryHandle = await window.showDirectoryPicker();

    const all = getAllFiles(); // This is again an array of 'File' objects
    for (const file of all) {
      const fileHandle = await directoryHandle.getFile(file.name, {create: true});
      const writable = await fileHandle.createWritable();
      await writable.write(file.content);
      await writable.close();
    }

    alert('Done');
  }

In this example I am using showDirectoryPicker() again because I want the user to choose a directory to save multiple files to. We get an handle to a directory and again we have an array of File objects. We create file handles in the directory and write our files content to them. If I would like to save only one file I could use showSaveFilePicker(options) with options like:

  const options = {
    types: [
      {
        description: 'Text Files',
        accept: {
          'text/plain': ['.txt'],
        },
      },
    ],
  };

Conclusion

I personally really like the Native File System API in it’s current form. It’s easy to use and allows us to build new kinds of applications in the browser which is awesome. I really hope this becomes a standard soon and other browsers like Firefox and Safari will implement this API.