File System Access API: Save Changes to Original File
- Average speed
- km/h
- Result
- Finish time
- 50 min
- You entered
- Distance
- 10 km
- You entered
The File System Access API lets a web app ask the user for permission to read and write a specific file, and then save changes directly back to that same file. You get a file handle from a file picker, keep it in memory, and when you want to save, you call write() on the handle’s FileSystemWritableFileStream to overwrite the original. This is the modern browser way to build “Open… Save…� features without forcing a download every time.
What the API Actually Does
Before the File System Access API, a web page could only read a user’s file (via <input type="file">) or write a new file (via a download). There was no way to modify the same file the user picked. The API changes that by introducing handles:
FileSystemFileHandle– represents a file on the user’s disk.FileSystemDirectoryHandle– represents a folder, so you can enumerate its contents.FileSystemWritableFileStream– a stream you can write to, which replaces the file’s content when you close it.
The key method is showOpenFilePicker(), which returns a handle after the user selects a file. Once you have the handle, you can call createWritable() to get a writable stream, write new data, and close it. The browser handles the low-level disk write, and the user has already granted permission by picking the file.
How Saving to the Original File Works
Here is the typical flow in JavaScript:
- Call
showOpenFilePicker()and store the returned handle. - Read the file content with
handle.getFile()and display it in the app. - When the user clicks “Save�, call
handle.createWritable()to get aFileSystemWritableFileStream. - Write the new content with
writable.write(data). - Close the stream with
writable.close()– this commits the changes to the original file.
A minimal example:
const handle = await window.showOpenFilePicker();
const writable = await handle.createWritable();
await writable.write('New content');
await writable.close();
That’s it. The file on the user’s disk is now changed. The browser shows a permission prompt the first time you access a handle, and you can call requestPermission() to re-ask if needed.
Why You Need a User Gesture
For security, the API only works after a user gesture (like a click on a “Open� button). You cannot silently grab a file handle on page load. This prevents malicious sites from reading or overwriting files without consent. The user must actively pick a file, and the browser may show a confirmation dialog. After the first pick, the handle remains usable for the session, but you should still check queryPermission() before writing.
Common Problems and How to Avoid Them
- Permission revoked: The user can revoke access in the browser’s site settings. Always check
queryPermission()and prompt again if needed. - File changed externally: If the file is modified by another program, your write may overwrite those changes. The API does not detect conflicts; you can compare timestamps or sizes before writing.
- Large files: Writing the entire file at once can be slow. Use
writable.write()with aBlobor stream chunks for better performance. - Browser support: The API is not available in Firefox or Safari. Always provide a fallback (e.g., download the file) for those browsers.
- File extension changes: If you need to save as a different format, you must use
showSaveFilePicker()to get a new handle.
Alternatives and Fallbacks
If the API is not supported, you can still let users save files with a download link. This works everywhere but always creates a new file, not an overwrite. For a “Save As� experience, you can use the showSaveFilePicker() method, which lets the user choose a location and filename. Some libraries like browser-fs-access provide a wrapper that falls back to downloads when the API is missing.
Security and Privacy Considerations
The API is designed with user consent at its core. The browser shows a permission prompt when you first request a handle, and the user can revoke it at any time. You should never store handles in a way that could be accessed by another origin; they are only valid within the same origin and session. Also, be careful when writing to files that contain sensitive data – the user is trusting you to not corrupt their work.
Frequently Asked Questions
Does the File System Access API work in all browsers?
No. It is supported in Chrome, Edge, and Opera (as of 2024), but not in Firefox or Safari. For those browsers, you need a fallback like a download link.
Can I write to a file without the user picking it first?
No. The user must select a file via showOpenFilePicker() or showSaveFilePicker(). This is a security requirement to prevent silent access.
What happens if the user edits the file while my app is open?
Your app will not see the changes unless you re-read the file. If you write, you will overwrite the external changes. You can check the file’s lastModified time before writing to warn the user.
Can I save to a file with a different name or extension?
Yes, but you need to call showSaveFilePicker() to let the user choose a new file. The original handle is fixed to the original file.
Is the API available in Web Workers?
No, the API is only available in the main thread of a window. You cannot use it in a service worker or a worker thread.
How do I request permission again after the user revoked it?
Call handle.requestPermission() with a user gesture. The browser will show a prompt, and if the user accepts, you can use the handle again.
Related
Formula used
in t | Finish time | min | 50 | 0 in d | Distance | km | 10 | 0 out pace | Average pace | min/km | t / d | 2 out speed | Average speed | km/h | d / (t / 60) | 2
Each line is one input (in, sel, list) or one result (out: label, unit, formula, decimals). How the calculators work
Method: the standard formula for this calculation, rounded as each result states. See how the calculators work or report an error.