The File System Access API: simplifying access to local files
The File System Access API allows web apps to read or save changes directly to files and folders on the user's device.
What is the File System Access API? #
The File System Access API (formerly known as Native File System API and prior to that it was called Writeable Files API) enables developers to build powerful web apps that interact with files on the user's local device, like IDEs, photo and video editors, text editors, and more. After a user grants a web app access, this API allows them to read or save changes directly to files and folders on the user's device. Beyond reading and writing files, the File System Access API provides the ability to open a directory and enumerate its contents.
If you've worked with reading and writing files before, much of what I'm about to share will be familiar to you. I encourage you to read it anyway, because not all systems are alike.
Current status #
Step | Status |
---|---|
1. Create explainer | Complete |
2. Create initial draft of specification | Complete |
3. Gather feedback & iterate on design | Complete |
4. Origin trial | Complete |
5. Launch | Complete |
Using the File System Access API #
To show off the power and usefulness of the File System Access API, I wrote a single file text editor. It lets you open a text file, edit it, save the changes back to disk, or start a new file and save the changes to disk. It's nothing fancy, but provides enough to help you understand the concepts.
Try it #
See the File System Access API in action in the text editor demo.
Read a file from the local file system #
The first use case I wanted to tackle was to ask the user to choose a file, then open and read that file from disk.
Ask the user to pick a file to read #
The entry point to the File System Access API is . When called, it shows a file picker dialog box, and prompts the user to select a file. After they select a file, the API returns an array of file handles. An optional parameter lets you influence the behavior of the file picker, for example, by allowing the user to select multiple files, or directories, or different file types. Without any options specified, the file picker allows the user to select a single file. This is perfect for a text editor.
Like many other powerful APIs, calling must be done in a secure context, and must be called from within a user gesture.
Once the user selects a file, returns an array of handles, in this case a one-element array with one that contains the properties and methods needed to interact with the file.
It's helpful to keep a reference to the file handle around so that it can be used later. It'll be needed to save changes back to the file, or to perform any other file operations.
Read a file from the file system #
Now that you have a handle to a file, you can get the file's properties, or access the file itself. For now, I'll simply read its contents. Calling returns a object, which contains a blob. To get the data from the blob, call one of its methods (, , , ).
The object returned by is only readable as long as the underlying file on disk hasn't changed. If the file on disk is modified, the object becomes unreadable and you'll need to call again to get a new object to read the changed data.
Putting it all together #
When users click the Open button, the browser shows a file picker. Once they've selected a file, the app reads the contents and puts them into a .
Write the file to the local file system #
In the text editor, there are two ways to save a file: Save, and Save As. Save simply writes the changes back to the original file using the file handle retrieved earlier. But Save As creates a new file, and thus requires a new file handle.
Create a new file #
To save a file, call , which will show the file picker in "save" mode, allowing the user to pick a new file they want to use for saving. For the text editor, I also wanted it to automatically add a extension, so I provided some additional parameters.
Save changes to disk #
You can find all the code for saving changes to a file in my text editor demo on GitHub. The core file system interactions are in . At its simplest, the process looks like the code below. I'll walk through each step and explain it.
Writing data to disk uses a object, essentially a . Create the stream by calling on the file handle object. When is called, the browser first checks if the user has granted write permission to the file. If permission to write hasn't been granted, the browser will prompt the user for permission. If permission isn't granted, will throw a , and the app will not be able to write to the file. In the text editor, these s are handled in the method.
The method takes a string, which is what's needed for a text editor. But it can also take a BufferSource, or a Blob. For example, you can pipe a stream directly to it:
You can also , or within the stream to update the file at a specific position, or resize the file.
Storing file handles in IndexedDB #
File handles are serializable, which means that you can save a file handle to IndexedDB, or call to send them between the same top-level origin.
Saving file handles to IndexedDB means that you can store state, or remember which files a user was working on. This makes it possible to keep a list of recently opened or edited files, offer to re-open the last file when the app is opened, etc. In the text editor, I store a list of the five most recent files the user has opened, making it easy to access those files again.
Since permissions currently are not persisted between sessions, you should verify whether the user has granted permission to the file using . If they haven't, use to (re-)request it.
In the text editor, I created a method that checks if the user has already granted permission, and if required, makes the request.
By requesting write permission with the read request, I reduced the number of permission prompts: the user sees one prompt when opening the file, and grants permission to both read and write to it.
Opening a directory and enumerating its contents #
To enumerate all files in a directory, call . The user selects a directory in a picker, after which a is returned, which lets you enumerate and access the directory's files.
Creating or accessing files and folders in a directory #
From a directory, you can create or access files and folders using the or respectively the method. By passing in an optional object with a key of and a boolean value of or , you can determine if a new file or folder should be created if it doesn't exist.
Resolving the path of an item in a directory #
When working with files or folders in a directory, it can be useful to resolve the path of the item in question. This can be done with the aptly named method. For resolving, the item can be a direct or indirect child of the directory.
Deleting files and folders in a directory #
If you have obtained access to a directory, you can delete the contained files and folders with the method. For folders, deletion can optionally be recursive and include all subfolders and the therein contained files.
Drag and drop integration #
The HTML Drag and Drop interfaces enable web applications to accept dragged and dropped files on a web page. During a drag and drop operation, dragged file and directory items are associated with file entries and directory entries respectively. The method returns a promise with a object if the dragged item is a file, and a promise with a object if the dragged item is a directory. The listing below shows this in action. Note that the Drag and Drop interface's will be for both files and directories, whereas the File System Access API's will be for files and for directories.
Accessing the origin-private file system #
The origin-private file system is a storage endpoint that, as the name suggests, is private to the origin of the page. While browsers will typically implement this by persisting the contents of this origin-private file system to disk somewhere, it is not intended that the contents be easily user accessible. Similarly, there is no expectation that files or directories with names matching the names of children of the origin-private file system exist. While the browser might make it seem that there are files, internally—since this is an origin-private file system—the browser might store these "files" in a database or any other data structure. Essentially: what you create with this API, do not expect to find it 1:1 somewhere on the hard disk. You can operate as usual on the origin-private file system once you have access to the root .
Polyfilling #
It is not possible to completely polyfill the File System Access API methods.
- The method can be approximated with an element.
- The method can be simulated with a element, albeit this will trigger a programmatic download and not allow for overwriting existing files.
- The method can be somewhat emulated with the non-standard element.
We have developed a library called browser-nativefs that uses the File System Access API wherever possible and that falls back to these next best options in all other cases.
Security and permissions #
The Chrome team has designed and implemented the File System Access API using the core principles defined in Controlling Access to Powerful Web Platform Features, including user control and transparency, and user ergonomics.
Opening a file or saving a new file #
When opening a file, the user provides permission to read a file or directory via the file picker. The open file picker can only be shown via a user gesture when served from a secure context. If users change their minds, they can cancel the selection in the file picker and the site does not get access to anything. This is the same behavior as that of the element.
Similarly, when a web app wants to save a new file, the browser will show the save file picker, allowing the user to specify the name and location of the new file. Since they are saving a new file to the device (versus overwriting an existing file), the file picker grants the app permission to write to the file.
Restricted folders #
To help protect users and their data, the browser may limit the user's ability to save to certain folders, for example, core operating system folders like Windows, the macOS Library folders, etc. When this happens, the browser will show a modal prompt and ask the user to choose a different folder.
Modifying an existing file or directory #
A web app cannot modify a file on disk without getting explicit permission from the user.
Permission prompt #
If a person wants to save changes to a file that they previously granted read access to, the browser will show a modal permission prompt, requesting permission for the site to write changes to disk. The permission request can only be triggered by a user gesture, for example, by clicking a Save button.
Alternatively, a web app that edits multiple files, like an IDE, can also ask for permission to save changes at the time of opening.
If the user chooses Cancel, and does not grant write access, the web app cannot save changes to the local file. It should provide an alternative method to allow the user to save their data, for example by providing a way to "download" the file, saving data to the cloud, etc.
Transparency #
Once a user has granted permission to a web app to save a local file, the browser will show an icon in the URL bar. Clicking on the icon opens a pop-over showing the list of files the user has given access to. The user can easily revoke that access if they choose.
Permission persistence #
The web app can continue to save changes to the file without prompting until all tabs for that origin are closed. Once a tab is closed, the site loses all access. The next time the user uses the web app, they will be re-prompted for access to the files.
Feedback #
We want to hear about your experiences with the File System Access API.
Tell us about the API design #
Is there something about the API that doesn't work like you expected? Or are there missing methods or properties that you need to implement your idea? Have a question or comment on the security model?
Problem with the implementation? #
Did you find a bug with Chrome's implementation? Or is the implementation different from the spec?
- File a bug at https://new.crbug.com. Be sure to include as much detail as you can, simple instructions for reproducing, and set Components to . Glitch works great for sharing quick and easy repros.
Planning to use the API? #
Planning to use the File System Access API on your site? Your public support helps us to prioritize features, and shows other browser vendors how critical it is to support them.
Helpful links #
Acknowledgements #
The File System Access API spec was written by Marijn Kruisselbrink.
-
-
-