Origin private file system
==========================

**Secure context:** This feature is available only in secure contexts (HTTPS), in some or all supporting browsers.

The origin private file system (OPFS) is a storage endpoint provided as part of the File System API, which is private to the origin of the page and not visible to the user like the regular file system. It provides access to a special kind of file that is highly optimized for performance and offers in-place write access to its content.

Working with files using the File System Access API
---------------------------------------------------

The File System Access API, which extends the File System API, provides access to files using picker methods. For example:

1. `Window.showOpenFilePicker()` allows the user to choose a file to access, which results in a `FileSystemFileHandle` object being returned.
2. `FileSystemFileHandle.getFile()` is called to get access to the file's contents, the content is modified using `FileSystemFileHandle.createWritable()` / `FileSystemWritableFileStream.write()`.
3. `FileSystemHandle.requestPermission({mode: 'readwrite'})` is used to request the user's permission to save the changes.
4. If the user accepts the permission request, the changes are saved back to the original file.

This works, but it has some restrictions. These changes are being made to the user-visible file system, so there are a lot of security checks in place (for example, safe browsing in Chrome) to guard against malicious content being written to that file system. These writes are not in-place, and instead use a temporary file. The original is not modified unless it passes all the security checks.

As a result, these operations are fairly slow. It is not so noticeable when you are making small text updates, but the performance suffers when making more significant, large-scale file updates such as SQLite database modifications.

How does the OPFS solve such problems?
--------------------------------------

The OPFS offers low-level, byte-by-byte file access, which is private to the origin of the page and not visible to the user. As a result, it doesn't require the same series of security checks and permission grants and is therefore faster than File System Access API calls. It also has a set of synchronous calls available (other File System API calls are asynchronous) that can be run inside web workers only so as not to block the main thread.

To summarize how the OPFS differs from the user-visible file system:

* The OPFS is subject to browser storage quota restrictions, just like any other origin-partitioned storage mechanism (for example IndexedDB API). You can access the amount of storage space the OPFS is using via `navigator.storage.estimate()`.
* Clearing storage data for the site deletes the OPFS.
* Permission prompts and security checks are not required to access files in the OPFS.
* Browsers persist the contents of the OPFS to disk somewhere, but you cannot expect to find the created files matched one-to-one. The OPFS is not intended to be visible to the user.
How do you access the OPFS?
---------------------------

To access the OPFS in the first place, you call the `navigator.storage.getDirectory()` method. This returns a reference to a `FileSystemDirectoryHandle` object that represents the root of the OPFS.

Manipulating the OPFS from the main thread
------------------------------------------

When accessing the OPFS from the main thread, you will use asynchronous, `Promise`-based APIs. You can access file (`FileSystemFileHandle`) and directory (`FileSystemDirectoryHandle`) handles by calling `FileSystemDirectoryHandle.getFileHandle()` and `FileSystemDirectoryHandle.getDirectoryHandle()` respectively on the `FileSystemDirectoryHandle` object representing the OPFS root (and child directories, as they are created).

**Note:** Passing `{ create: true }` into the above methods causes the file or folder to be created if it doesn't exist.


```js
// Create a hierarchy of files and folders
const fileHandle = await opfsRoot.getFileHandle("my first file", {
  create: true,
});
const directoryHandle = await opfsRoot.getDirectoryHandle("my first folder", {
  create: true,
});
const nestedFileHandle = await directoryHandle.getFileHandle(
  "my first nested file",
  { create: true },
);
const nestedDirectoryHandle = await directoryHandle.getDirectoryHandle(
  "my first nested folder",
  { create: true },
);

// Access existing files and folders via their names
const existingFileHandle = await opfsRoot.getFileHandle("my first file");
const existingDirectoryHandle =
  await opfsRoot.getDirectoryHandle("my first folder");
```

### Reading a file

1. Make a `FileSystemDirectoryHandle.getFileHandle()` call to return a `FileSystemFileHandle` object.
2. Call the `FileSystemFileHandle.getFile()` object to return a `File` object. This is a specialized type of `Blob`, and as such can be manipulated just like any other `Blob`. For example, you could access the text content directly via `Blob.text()`.
### Writing a file

1. Make a `FileSystemDirectoryHandle.getFileHandle()` call to return a `FileSystemFileHandle` object.
2. Call `FileSystemFileHandle.createWritable()` to return a `FileSystemWritableFileStream` object, which is a specialized type of `WritableStream`.
3. Write contents to it using a `FileSystemWritableFilestream.write()` call.
4. Close the stream using `WritableStream.close()`.
### Deleting a file or folder

You can call `FileSystemDirectoryHandle.removeEntry()` on the parent directory, passing it the name of the item you want to remove:


```js
directoryHandle.removeEntry("my first nested file");
```


You can also call `FileSystemHandle.remove()` on the `FileSystemFileHandle` or `FileSystemDirectoryHandle` representing the item you want to remove. To delete a folder including all subfolders, pass the `{ recursive: true }` option.


```js
await fileHandle.remove();
await directoryHandle.remove({ recursive: true });
```


The following provides a quick way to clear the entire OPFS:


```js
await (await navigator.storage.getDirectory()).remove({ recursive: true });
```

### Listing the contents of a folder

`FileSystemDirectoryHandle` is an asynchronous iterator. As such, you can iterate over it with a `for await…of` loop and standard methods such as `entries()`, `values()`, and `keys()`.

For example:


```js
for await (let [name, handle] of directoryHandle) {
}
for await (let [name, handle] of directoryHandle.entries()) {
}
for await (let handle of directoryHandle.values()) {
}
for await (let name of directoryHandle.keys()) {
}
```

Manipulating the OPFS from a web worker
---------------------------------------

Web Workers don't block the main thread, which means you can use the synchronous file access APIs in this context. Synchronous APIs are faster as they avoid having to deal with promises.

You can synchronously access a file by calling `FileSystemFileHandle.createSyncAccessHandle()` on a regular `FileSystemFileHandle`:

**Note:** Despite having "Sync" in its name, the `createSyncAccessHandle()` method itself is asynchronous.


```js
const opfsRoot = await navigator.storage.getDirectory();
const fileHandle = await opfsRoot.getFileHandle("my highspeed file.txt", {
  create: true,
});
const syncAccessHandle = await fileHandle.createSyncAccessHandle();
```


There are a number of *synchronous* methods available on the returned `FileSystemSyncAccessHandle`:

* `getSize()`: Returns the size of the file in bytes.
* `write()`: Writes the content of a buffer into the file, optionally at a given offset, and returns the number of written bytes. Checking the returned number of written bytes allows callers to detect and handle errors and partial writes.
* `read()`: Reads the contents of the file into a buffer, optionally at a given offset.
* `truncate()`: Resizes the file to the given size.
* `flush()`: Ensures that the file contents contain all the modifications done through `write()`.
* `close()`: Closes the access handle.

Here is an example that uses all the methods mentioned above:


```js
const opfsRoot = await navigator.storage.getDirectory();
const fileHandle = await opfsRoot.getFileHandle("fast", { create: true });
const accessHandle = await fileHandle.createSyncAccessHandle();

const textEncoder = new TextEncoder();
const textDecoder = new TextDecoder();

// Initialize this variable for the size of the file.
let size;
// The current size of the file, initially `0`.
size = accessHandle.getSize();
// Encode content to write to the file.
const content = textEncoder.encode("Some text");
// Write the content at the beginning of the file.
accessHandle.write(content, { at: size });
// Flush the changes.
accessHandle.flush();
// The current size of the file, now `9` (the length of "Some text").
size = accessHandle.getSize();

// Encode more content to write to the file.
const moreContent = textEncoder.encode("More content");
// Write the content at the end of the file.
accessHandle.write(moreContent, { at: size });
// Flush the changes.
accessHandle.flush();
// The current size of the file, now `21` (the length of
// "Some textMore content").
size = accessHandle.getSize();

// Prepare a data view of the length of the file.
const dataView = new DataView(new ArrayBuffer(size));

// Read the entire file into the data view.
accessHandle.read(dataView);
// Logs `"Some textMore content"`.
console.log(textDecoder.decode(dataView));

// Read starting at offset 9 into the data view.
accessHandle.read(dataView, { at: 9 });
// Logs `"More content"`.
console.log(textDecoder.decode(dataView));

// Truncate the file after 4 bytes.
accessHandle.truncate(4);
```

Browser compatibility
---------------------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

See also
--------

* The origin private file system on web.dev