javascriptfile-system-access-apifilesystem-access

How can I save changes to a file selected from the user's computer in JavaScript?


Many modern web applications and PWAs allow users select a file from their hard drive, and then save changes back to that file directly.

An old-school approach was to read the file, make the required changes on the server side, and then send the user to a page where they can download the new file. However, this new generation of web apps is able to save the changes directly (no downloads required), and without any server involvement.

How is this possible, and how can I implement something similar?


Solution

  • The File System Access API allows you to open and read files (and even entire directories!) on the user's computer, and then write your changes back. If you choose to open a directory, it even has the ability to create and delete new files and folders within that directory!

    A nice introduction to this API can be found on Chrome's website. Otherwise, here's a simple example of how to read a singular file, and then save the changes back directly:

    let fileHandle;
    
    async function openFile() {
      [fileHandle] = await window.showOpenFilePicker();
    
      // we don't want to handle e.g. folders in this example
      if (fileHandle.kind !== "file") {
        alert("Please select a file, not a folder");
        return;
      }
    
      const file = await fileHandle.getFile();
      const contents = await file.text();
    
      document.querySelector("#contents").value = contents;
    }
    
    async function saveFile() {
      // Request permission to edit the file
      await fileHandle.requestPermission({ mode: "readwrite" });
    
      const writable = await fileHandle.createWritable();
      await writable.write(document.querySelector("#contents").value);
      await writable.close();
    }
    
    document.querySelector("#openButton").addEventListener("click", openFile);
    document.querySelector("#saveButton").addEventListener("click", saveFile);
    <p>
      <strong>Note: this does work, but StackOverflow's snippets block access to this API--- try it out on your local machine</strong>
    </p>
    
    <div>
      <button id="openButton">Open</button>
      <button id="saveButton">Save</button>
    </div>
    
    <textarea id="contents"></textarea>

    Key points: