{"id":363339,"date":"2022-02-14T08:01:21","date_gmt":"2022-02-14T16:01:21","guid":{"rendered":"https:\/\/css-tricks.com\/?p=363339"},"modified":"2022-02-14T08:02:48","modified_gmt":"2022-02-14T16:02:48","slug":"getting-started-with-the-file-system-access-api","status":"publish","type":"post","link":"https:\/\/css-tricks.com\/getting-started-with-the-file-system-access-api\/","title":{"rendered":"Getting Started With the File System Access API"},"content":{"rendered":"\n
The File System Access API is a web API that allows read and write access to a user’s local files. It unlocks new capabilities to build powerful web applications, such as text editors or IDE<\/abbr>s, image editing tools, improved import\/export, all in the frontend. Let\u2019s look into how to get started using this API.<\/p>\n\n\n\n\n\n\n\n Before diving into the code required to read a file from the user’s system, an important detail to keep in mind is that calling the File System Access API needs to be done by a user gesture, in a secure context<\/strong>. In the following example, we’ll use a click event.<\/p>\n\n\n Reading data from a file can be done in less than 10 lines of code. Here’s an example code sample:<\/p>\n\n\n\n Let’s imagine we have a button in our HTML with the class What we get back from calling These objects contain a The On Finally, we can call To read from multiple files, we need to pass an For example:<\/p>\n\n\n\n By default, the For example, if we only wanted to accept In this example, The File System Access API also allows you to write content to files. First, let’s look into how to save a new file.<\/p>\n\n\n Writing to a new file can also be done in a very short amount of code!<\/p>\n\n\n\n If we imagine a second button with the class Calling this method will also return a Finally, we need to call the If you wanted to write some HTML code to a file for example, you would only need to change what’s in the If you’d like to import a file and edit it with the File System Access API, an example code sample would look like:<\/p>\n\n\n\n If you’ve been following the rest of this post, you might recognize that we start with the If the file you’re importing already has content, this code sample will replace the current content with the new one passed into the Without going into too much detail, the File System Access API also lets you list files in directories and delete files or directories.<\/p>\n\n\n Reading directories can be done with a tiny bit of code:<\/p>\n\n\n\n If we add a new button with the class Deleting a file in a directory can be done with the following code sample:<\/p>\n\n\n\n If you want to delete a folder, you only need to make a small change to the code sample above:<\/p>\n\n\n\n Finally, if you want to remove a specific file when selecting a folder, you could write it like this:<\/p>\n\n\n\n And if you want to remove an entire folder, you would need the following lines:<\/p>\n\n\n\n At the moment, IE and Firefox don’t seem to be supporting the File System Access API. However, there exists a ponyfill<\/a> called browser-fs-access<\/a>.<\/p>\n\n\n This browser support data is from Caniuse<\/a>, which has more detail. A number indicates that browser supports the feature at that version and up.<\/p><\/div>Reading files with the File System Access API<\/h3>\n\n\n
Reading from a single file<\/h4>\n\n\n
let fileHandle;\n \ndocument.querySelector(\".pick-file\").onclick = async () => {\n [fileHandle] = await window.showOpenFilePicker();\n \n const file = await fileHandle.getFile();\n const content = await file.text();\n \n return content;\n};<\/code><\/pre>\n\n\n\n
.pick-file<\/code>. When clicking on this button, we launch the file picker by calling
window.showOpenFilePicker()<\/code>, and we store the result from this query in a variable called
fileHandle<\/code>. <\/p>\n\n\n\n
showOpenFilePicker()<\/code> is an array of
FileSystemFileHandle<\/code><\/a> objects representing each file we selected. As this example is for a single file, we destructure the result. I’ll show how to select multiple files a bit later.<\/p>\n\n\n\n
kind<\/code> and
name<\/code> property. If you were to use
console.log(fileHandle)<\/code>, you would see the following object:<\/p>\n\n\n\n
FileSystemFileHandle {kind: 'file', name: 'data.txt'}<\/code><\/pre>\n\n\n\n
kind<\/code> can either be
file<\/code> or
directory<\/code>.<\/p>\n\n\n\n
fileHandle<\/code>, we can then call the
getFile()<\/code> method to get details about our file. Calling this method returns an object with a few properties, including a timestamp of when the file was last modified, the name of the file, its size, and type.<\/p>\n\n\n\n
text()<\/code> on the file to get its content.<\/p>\n\n\n
Reading from multiple files<\/h4>\n\n\n
options<\/code> object to
showOpenFilePicker()<\/code>.<\/p>\n\n\n\n
let fileHandles;\nconst options = {\n multiple: true,\n};\n \ndocument.querySelector(\".pick-file\").onclick = async () => {\n fileHandles = await window.showOpenFilePicker(options);\n \n \/\/ The rest of the code will be shown below\n};<\/code><\/pre>\n\n\n\n
multiple<\/code> property is set to
false<\/code>. Other options can be used to indicate the types of files that can be selected.<\/p>\n\n\n\n
.jpeg<\/code> files, the options object would include the following:<\/p>\n\n\n\n
const options = {\n types: [\n {\n description: \"Images\",\n accept: {\n \"image\/jpeg\": \".jpeg\",\n },\n },\n ],\n excludeAcceptAllOption: true,\n};<\/code><\/pre>\n\n\n\n
fileHandles<\/code> is an array containing multiple files, so getting their content would be done in the following way:<\/p>\n\n\n\n
let fileHandles;\nconst options = {\n multiple: true,\n};\n \ndocument.querySelector(\".pick-file\").onclick = async () => {\n fileHandles = await window.showOpenFilePicker(options);\n \n const allContent = await Promise.all(\n fileHandles.map(async (fileHandle) => {\n const file = await fileHandle.getFile();\n const content = await file.text();\n return content;\n })\n );\n \n console.log(allContent);\n};<\/code><\/pre>\n\n\n
Writing to a file with the File System Access API<\/h3>\n\n\n
Writing to a new file<\/h4>\n\n\n
document.querySelector(\".save-file\").onclick = async () => {\n const options = {\n types: [\n {\n description: \"Test files\",\n accept: {\n \"text\/plain\": [\".txt\"],\n },\n },\n ],\n };\n \n const handle = await window.showSaveFilePicker(options);\n const writable = await handle.createWritable();\n \n await writable.write(\"Hello World\");\n await writable.close();\n \n return handle;\n};<\/code><\/pre>\n\n\n\n
save-file<\/code>, on click, we open the file picker with the method
showSaveFilePicker()<\/code> and we pass in an
option<\/code> object containing the type of file to be saved, here a
.txt<\/code> file.<\/p>\n\n\n\n
FileSystemFileHandle<\/code> object like in the first section. On this object, we can call the
createWritable()<\/code> method that will return a
FileSystemWritableFileStream<\/code> object. We can then write some content to this stream with the
write()<\/code> method in which we need to pass the content.<\/p>\n\n\n\n
close()<\/code> method to close the file and finish writing the content to disk.<\/p>\n\n\n\n
options<\/code> object to accept
\"text\/html\": [\".html\"]<\/code> and pass some HTML content to the
write()<\/code> method.<\/p>\n\n\n
Editing an existing file<\/h4>\n\n\n
let fileHandle;\n \ndocument.querySelector(\".pick-file\").onclick = async () => {\n [fileHandle] = await window.showOpenFilePicker();\n \n const file = await fileHandle.getFile();\n const writable = await fileHandle.createWritable();\n \n await writable.write(\"This is a new line\");\n await writable.close();\n};<\/code><\/pre>\n\n\n\n
showOpenFilePicker()<\/code> and
getFile()<\/code> methods to read a file and we then use
createWritable()<\/code>,
write()<\/code> and
close()<\/code> to write to that same file.<\/p>\n\n\n\n
write()<\/code> method.<\/p>\n\n\n
Additional File System Access API features<\/h3>\n\n\n
Read directories<\/h4>\n\n\n
document.querySelector(\".read-dir\").onclick = async () => {\n const directoryHandle = await window.showDirectoryPicker();\n \n for await (const entry of directoryHandle.values()) {\n console.log(entry.kind, entry.name);\n }\n};<\/code><\/pre>\n\n\n\n
.read-dir<\/code>, on click, calling the
showDirectoryPicker()<\/code> method will open the file picker and, when selecting a directory on your computer, this code will list the files found in that directory.<\/p>\n\n\n
Delete files<\/h4>\n\n\n
document.querySelector(\".pick-file\").onclick = async () => {\n const [fileHandle] = await window.showOpenFilePicker();\n await fileHandle.remove();\n};<\/code><\/pre>\n\n\n\n
document.querySelector(\".read-dir\").onclick = async () => {\n const directoryHandle = await window.showDirectoryPicker();\n await directoryHandle.remove();\n};<\/code><\/pre>\n\n\n\n
\/\/ Delete a single file named data.txt in the selected folder\ndocument.querySelector(\".pick-folder\").onclick = async () => {\n const directoryHandle = await window.showDirectoryPicker();\n await directoryHandle.removeEntry(\"data.txt\");\n};<\/code><\/pre>\n\n\n\n
\/\/ Recursively delete the folder named \"data\"\ndocument.querySelector(\".pick-folder\").onclick = async () => {\n const directoryHandle = await window.showDirectoryPicker();\n await directoryHandle.removeEntry('data', { recursive: true });\n};<\/code><\/pre>\n\n\n
File System Access API browser support<\/h3>\n\n\n
Desktop<\/h4>