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:
- User Action Required: File pickers cannot be triggered programmatically without direct user interaction (e.g., a click event).
- Explicit Permission Checks: Browsers prompt the
user separately for read and write permissions. You can query or request
permissions explicitly using
fileHandle.queryPermission({ mode: 'readwrite' })andfileHandle.requestPermission({ mode: 'readwrite' }). - Restricted Directories: Browsers block access to sensitive system paths (like the Windows or System32 folders) to prevent unauthorized modifications to critical system files.