An OPFS wrapper providing path support and an API based on Unix commands.
npm install opfsxbash
npm install opfsx
`Usage
OPFSX provides "commands" which can be used on the main thread and within workers.
A list of all available commands can be found below with basic code snippets and example usages can be found in ./examples.$3
You can import all commands or specific commands individually:
`ts
import * as opfsx from "opfsx"await opfsx.write("hello.txt", "hello world")
await opfsx.tree("/")
`
`ts
import {write, tree} from "opsfx"write("hello.txt", "hello world")
tree("/")
`$3
A path like /example/path or even /example/path.txt is ambiguous and could refer to a file or directory.
It's obvious for most commands if a path should be treated as a file or directory (mkdir, ls, tree, etc), however in situations where there could be ambiguity a trailing slash is used to signal that the path is intended as a directory.Commands
$3
Resolve the given path into a FileSystemDirectoryHandle or FileSystemFileHandle.
A path with a trailing slash will resolve to a directory and paths without will resolve to a file.The default behaviour is to throw an error if any directory/file doesn't exist, passing the
create: true option will instead create all required directories and potentially the file itself, before returning the handle of the new directory/file.`ts
import * as opfs from "opfsx"const fileHandle = await opfs.resolve("/example/path/test.txt")
// will resolve to file as there is no trailing slash
const fileHandle2 = await opfs.resolve("/example/path/test", {create: true})
// will cause any missing directories to be created, then return the handle for the 'yet.txt' directory
const directoryHandle = await opfs.resolve("/example/not/create/yet.txt/", {create: true})
`The
resolve command is useful if you wish to make use of the faster FileSystemSyncAccessHandle within a worker too:
`ts
import * as opfs from "opfsx"const fileHandle = opfs.resolve("/example/path/test.txt")
const syncAccessHandle = await fileHandle.createSyncAccessHandle();
`$3
Write a string, Blob or File to the given file path. This command will recursively create any missing directories and the file itself if missing.Write text content to a file:
`ts
import * as opfs from "opfsx"await opfs.write("/example/path/test.txt", "hello world")
`Write a file:
`ts
import * as opfs from "opfsx"const markdownFile = new File(["# hello world \n this is some example markdown"], "hello.md", {type: "text/markdown"})
await opfs.write(
/example/path/hello.md, markdownFile)
`You are not limited to just text files, you could write things like images too:
`ts
import * as opfs from "opfsx"// assuming an input accepting image files...
const profileImage = document.getElementById("profile-image").files[0];
await opfs.write(
/images/profile/${profileImage.name}, profileImage)
`$3
Read a file, returning the File instance retrieved from FileSystemFileHandle.getFile().
If you are writing code within a worker, you may want to consider fetching the handle via resolve() and using FileSystemSyncAccessHandle instead.`ts
import * as opfs from "opfsx"const file = await opfs.read("/example/path/hello.md")
const content = await file.text()
console.log(file.name) // hello.md
console.log(content) // hello world
`Construct URL from an image file:
`ts
import * as opfs from "opfsx"const file = await opfs.read("/images/example.png")
const url = URL.createObjectURL(file)
console.log(file.name) // example.png
console.log(url) // something like 'blob:https://example.com/7bf69880-a2d0-4a12-81a8-56637cc80b23' which you could now add to an
element
`$3
List items in a directory. Only direct children are returned by default, but you can pass the recursive: true flag to also include all subdirectories and their files.
This command always returns a flat array, even with the recursive options set. If you wish to list all content while preserving the directory structure, use the tree() command instead.
In future this command may support some form on built-in filtering, but for now it will always return all files and directories.`ts
import * as opfs from "opfsx"// List files and directoreis at the given location
await opfs.ls("/example/path")
// List all .md files in a directory and all subdiretories
const items = await opfs.ls("/example/nested", {recursive: true})
const markdownFiles = items.filter(item => item.kind === 'file' && item.name.endsWith('.md'))
`Recursive list .md files in a directory:
`ts
import * as opfs from "opfsx"const items = await opfs.ls("/example", {recursive: true})
const markdownFiles = items.filter(item => item.kind === 'file' && item.name.endsWith('.md'))
`$3
Get the "tree" structure of a directory, recursively reading all subdirectories and files.`ts
import * as opfs from "opfsx"await opfs.tree("/example")
`$3
Create a directory, recursively creating any folders in the path that don't exist yet.`ts
import * as opfs from "opfsx"await opfs.mkdir("/example/nested/path")
`$3
Remove a file or directory. To remove a directory, you must pass the recursive: true option.`ts
import * as opfs from "opfsx"await opfs.rm("/example/hello.md")
// Pass 'recursive' option to delete a directory
await opfs.rm("/", {recursive: true})
`$3
Copy a file or directory to another location, will fail if any directory in destination path doesn't exist unless passing create: true option.
In cases where the destination path could be interpreted as either a file or directory, a trailing slash is used to signal that the path is a directory.Copy a file:
`ts
import * as opfs from "opfsx"await opfs.cp("/example/hello.md", "/example2/hello.md")
`Copy an entire directory:
`ts
import * as opfs from "opfsx"// assuming /example2 doesn't exist yet, create option is required here
await opfs.cp("/example", "/example2/nested", {create: true})
`Handling ambiguous paths:
`ts
import * as opfs from "opfsx"// test.md would be copied to a file called 'nested' with no file extension:
await opfs.cp("/example/test.md", "/example2/nested")
// test.md would be copied into the directory called 'nested'. This requires the
create option if the directory doesn't exist yet.
await opfs.cp("/example/test.md", "/example2/nested/", {create: true})// would copy 'folder' into the 'nested' directory
await opfs.cp("/example/folder", "/example2/nested", {create: true})
`$3
OPFS has no concept of moving files, so this command just runs cp() then rm().
The operation will fail if any directory in destination path doesn't exist unless passing create: true option.
Ambiguous paths are handled just like cp(), using a trailing slash to signal a directory.`ts
import * as opfs from "opfsx"await opfs.mv("/example/hello.md", "/example2/hello.md")
await opfs.mv("/example/nested/test1", "/example/new-folder", {create: true})
`$3
Get metadata about a given directory or file:
`ts
import * as opfs from "opfsx"await opfs.stat("/example/nested/test1")
``