Read and Edit Local Files with File System Access API

The File System Access API allows web applications to read, modify, and save files directly on a user’s local device without relying on traditional upload and download workflows. By requesting user permission through native file dialogs, the browser grants the web page a secure handle to a specific file or directory. This article explains how JavaScript obtains file handles, reads file data, writes modifications back to disk, and manages user permissions.

Requesting File Access

To access a local file, JavaScript uses the window.showOpenFilePicker() method. Calling this function prompts the user with the operating system’s native file picker dialog. Because this action accesses local storage, it must be triggered by a direct user gesture, such as clicking a button.

const [fileHandle] = await window.showOpenFilePicker({
  types: [
    {
      description: 'Text Files',
      accept: {
        'text/plain': ['.txt'],
      },
    },
  ],
  multiple: false,
});

This method returns an array of FileSystemFileHandle objects representing the selected files. The handle acts as a reference to the file on disk without loading the entire file into memory immediately.

Reading File Contents

Once a FileSystemFileHandle is obtained, you can access the file’s data by calling its getFile() method. This returns a standard File object containing the file’s metadata and contents.

// Get the File object
const file = await fileHandle.getFile();

// Read the text content
const contents = await file.text();
console.log(contents);

You can read the file data using built-in methods on the File object, such as .text() for strings, .arrayBuffer() for binary data, or .stream() for streaming large files in chunks.

Editing and Writing to the File

To modify the file on disk, you must create a writable stream using fileHandle.createWritable(). This operation initiates a request for write permissions if they have not already been granted.

// Create a FileSystemWritableFileStream
const writable = await fileHandle.createWritable();

// Write new content to the stream
await writable.write('Updated file content.');

// Close the stream to save changes to disk
await writable.close();

When you write to the stream, the API creates a temporary swap file in the background. Calling writable.close() flushes the changes, verifies integrity, and atomically replaces the original file with the updated version.

Saving as a New File

If you want to save data to a new file rather than modifying an existing one, use window.showSaveFilePicker(). This opens a native “Save As” dialog and returns a new FileSystemFileHandle.

const newHandle = await window.showSaveFilePicker({
  suggestedName: 'document.txt',
});

const writable = await newHandle.createWritable();
await writable.write('New file contents.');
await writable.close();

Security and Permission Handling

The File System Access API is designed with explicit security boundaries: