In this post you will learn how to use the Origin Private File System with JavaScript to store, read and remove any file on the web browser.
You may try the demo here: https://stackblitz.com/edit/vitejs-vite-hl34zf?file=index.html
As I said before, the Origin Private File System has arrived to revolutionize things with JavaScript. Thanks to OPFS we can have a complete file system with JavaScript directly in the web browser.
With this new technology, we can write any type of file in the web browser, as well as download it later. All of this without depending on localStorage or similar things; It is a different technology.
Text documents, images, videos and even databases can be saved, and there is no need to ask the user for permission or confirmation, everything is transparent.
Root folder
In order to access to the root folder we call navigator.storage.getDirectory()
. This is the root folder, here we can create more folders and files. I will create a folder with the following code:
const directoryName = "pictures";
const opfsRoot = await navigator.storage.getDirectory();
const picturesFolder = await opfsRoot.getDirectoryHandle(directoryName, {
create: true,
});
From here, I’m going to store, read, and delete from picturesFolder
. Keep in mind that a “handle” is not the file or directory itself, but its handle as the name suggests.
To get the file we call getFile
and similar things, as we will see later.
Store file in the web browser with JavaScript
Now that we have a directory handle we can call getFileHandle
(setting the create
option to true
), then call createWritable
and finally calling write
.
You should pass an ArrayBuffer or blob to the write
function, we do not need to convert the files. The file can come from anywhere; for example, from a web server or from a file type input.
In this example I am going to use a file type input and the function looks like this:
const storeFile = async (file, folder) => {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = async () => {
const fileName = file.name;
const fileHandle = await folder.getFileHandle(fileName, {
create: true,
});
const writable = await fileHandle.createWritable();
await writable.write(reader.result);
await writable.close();
resolve();
};
reader.onerror = error => {
reject(error);
}
reader.readAsArrayBuffer(file);
});
}
We can call this function from the input change
event or any event (like a click
on a button
):
$fileSelect.addEventListener("change", async () => {
const files = $fileSelect.files;
for (const file of files) {
await storeFile(file, picturesFolder)
}
$fileSelect.value = "";
renderFiles(picturesFolder);
});
List files in a folder
Again, once we have an OPFS handler, we can iterate it with a for await
loop, getting the name
and the handler
of each file inside that directory or folder. The simplest for await
loop is like this:
for await (let [name, handle] of folder) {
//
}
Here I am using vanilla JavaScript to render the stored files, adding the listeners to download and remove each file.
const renderFiles = async (folder) => {
while ($mainContainer.firstChild) {
$mainContainer.removeChild($mainContainer.firstChild);
}
for await (let [name, handle] of folder) {
const container = Object.assign(document.createElement("div"));
const anchorFileName = Object.assign(document.createElement("a"), {
textContent: name,
href: "javascript:void(0)",
classList: ["file"],
});
const anchorRemove = Object.assign(document.createElement("a"), {
textContent: "Eliminar",
href: "javascript:void(0)",
classList: ["remove"],
});
anchorFileName.addEventListener("click", () => {
downloadFile(name, folder);
});
anchorRemove.addEventListener("click", async () => {
await removeFile(name, folder);
await renderFiles(folder);
})
container.appendChild(anchorFileName);
container.appendChild(anchorRemove);
$mainContainer.append(container);
}
};
Note: Although I use the word “download” here, there is no need for servers or similar things. Everything is offline, what we do when downloading the file is read it from the browser and ask the user the location where they want to save it.
Download a file from Origin Private File System
Previously we learned how to store a file in the browser with JavaScript and also how to obtain the list of files that exist within a directory.
Let’s see how to recover a file. Here we are going to download it, but we can send it to a server or make anything else with it.
const downloadFile = async (fileName, folder) => {
const fileHandle = await folder.getFileHandle(fileName);
const file = await fileHandle.getFile();
const a = document.createElement("a");
const objectUrl = URL.createObjectURL(file);
a.href = objectUrl;
a.download = fileName;
a.click();
URL.revokeObjectURL(objectUrl);
}
The actual file is in the file
constant, and then we simply create a link to download it with URL.createObjectURL
. Here we could do anything else with the file, for example, show it to the user, send it to a server and so on.
Remove file
Finally let’s see how to delete a file previously stored in the web browser with JavaScript. Once we have its handler we just invoke remove
(note that, at the time of writing this post, remove
only works in Chrome and Edge):
const removeFile = async (fileName, folder) => {
if (!('remove' in FileSystemFileHandle.prototype)) {
return alert("Tu navegador no soporta la eliminación de archivos");
}
if (!confirm('¿De verdad quieres eliminarlo?')) {
return;
}
const fileHandle = await folder.getFileHandle(fileName);
await fileHandle.remove();
}
Why did I say it is a revolution?
OPFS has brought with it two things: the file system itself, and the possibility for several existing libraries to use it.
What I liked the most is that SQLite3 now works perfectly in the web browser, so we are closer to creating serverless web applications (for cases when no server is needed) since we can have a database and also store files.
Conclusion and example
I have shown you the most important functions to use the Origin Private File System, it is a very simple example but it tries to teach you the most important thing. If you want to see the full code go to my GitHub: https://github.com/parzibyte/hello-opfs-js
You can try the example on any server of your choice, just make sure to send the COOP and COEP headers. When you run the example, the list of files and a field will appear so you can store more files (as in the image at the beginning of the post)
From this example we can do more complex things, since we already have an entire file system in the web browser that we can access with JavaScript.
We can even encrypt the files or display them as an image or video; the possibilities are endless.
I hope to bring a more complete application in the future, in the meantime I leave you with more JavaScript posts.