File System Access API: JavaScript File Editing

The File System Access API enables web applications to read, modify, and save files directly on a user’s local device, bridging the gap between web and native desktop applications. This article breaks down the purpose of the API, explains its core mechanisms, and demonstrates how JavaScript requests file handles to open, edit, and persist changes to local files.

What is the File System Access API?

The File System Access API is a modern browser API that gives web applications direct access to the user’s local file system upon receiving user permission. Historically, web apps relied on standard file inputs (<input type="file">) to read data and simulated downloads via anchor tags to save data. The File System Access API replaces this clunky workflow by granting read and write access to specific files and directories, allowing seamless in-place editing.

The API relies on two primary interfaces: - FileSystemFileHandle: Represents a reference to a specific file on disk. - FileSystemDirectoryHandle: Represents a reference to a directory on disk.

Requesting a File Handle

To interact with a file, you must first obtain a FileSystemFileHandle. Because accessing the file system involves user privacy, the request must be triggered by a direct user action, such as a button click, and executed within a secure context (HTTPS).

You request a file handle using window.showOpenFilePicker():

async function getFileHandle() {
  const options = {
    types: [
      {
        description: 'Text Files',
        accept: {
          'text/plain': ['.txt', '.md'],
        },
      },
    ],
    excludeAcceptAllOption: false,
    multiple: false,
  };

  // Open the native file picker
  const [fileHandle] = await window.showOpenFilePicker(options);
  return fileHandle;
}

Reading File Contents

Once you have the FileSystemFileHandle, you can retrieve the standard File object using the getFile() method and read its contents with standard web APIs:

async function readFile(fileHandle) {
  const file = await fileHandle.getFile();
  const contents = await file.text();
  return contents;
}

Modifying and Writing to the File

To save changes directly back to the original file, you must request a writable stream from the file handle via createWritable(). This triggers a permission prompt if write permissions have not yet been granted.

async function writeFile(fileHandle, contents) {
  // Create a FileSystemWritableFileStream to write to
  const writable = await fileHandle.createWritable();

  // Write the updated data
  await writable.write(contents);

  // Close the file and persist changes to disk
  await writable.close();
}

Creating or Saving a New File

If you need to save content to a new file rather than editing an existing one, use window.showSaveFilePicker() to prompt the user for a destination and file name:

async function saveNewFile(contents) {
  const options = {
    suggestedName: 'untitled.txt',
    types: [
      {
        description: 'Text Files',
        accept: { 'text/plain': ['.txt'] },
      },
    ],
  };

  const fileHandle = await window.showSaveFilePicker(options);
  await writeFile(fileHandle, contents);
}

Complete Workflow Example

Combining these steps allows for a full read-edit-write cycle:

document.getElementById('editButton').addEventListener('click', async () => {
  try {
    // 1. Prompt user to select a file
    const [fileHandle] = await window.showOpenFilePicker();

    // 2. Read the file
    const file = await fileHandle.getFile();
    let text = await file.text();

    // 3. Modify the content
    text += '\nUpdated by JavaScript via File System Access API.';

    // 4. Save the modified content back to the original file
    const writable = await fileHandle.createWritable();
    await writable.write(text);
    await writable.close();

    console.log('File successfully updated on disk.');
  } catch (error) {
    if (error.name !== 'AbortError') {
      console.error('File operation failed:', error);
    }
  }
});

Security and Permissions

Browsers enforce strict security boundaries when interacting with the File System Access API: - Explicit User Intent: Operations must originate from an explicit user interaction (e.g., click events). - Origin-Scoped Permissions: Read and write permissions are requested per-file or per-directory and are scoped to the site origin. - Restricted Directories: Access to sensitive operating system folders (such as system files or browser data) is automatically blocked by the browser.