Система частных файлов происхождения
Безопасный контекст: Эта функция доступна только в безопасных контекстах (HTTPS), в некоторых или всех поддерживающих браузерах.
Примечание: Эта функция доступна в Потоках Web Workers.
Система частных файлов происхождения (OPFS) — это конечная точка хранения, предоставляемая в рамках API файловой системы, которая является частной для происхождения страницы и не отображается пользователю, как обычная файловая система. Она предоставляет доступ к специальному виду файлов, высоко оптимизированному для производительности, и обеспечивает доступ к содержимому файла в месте его записи.
Работа с файлами с помощью API доступа к файловой системе
API доступа к файловой системе, который расширяет API файловой системы, предоставляет доступ к файлам с помощью методов выбора файлов. Например:
-
Window.showOpenFilePicker()позволяет пользователю выбрать файл для доступа, что приводит к возврату объектаFileSystemFileHandle. -
FileSystemFileHandle.getFile()вызывается для получения доступа к содержимому файла, содержимое изменяется с помощьюFileSystemFileHandle.createWritable()/FileSystemWritableFileStream.write(). -
FileSystemHandle.requestPermission({mode: 'readwrite'})используется для запроса разрешения пользователя на сохранение изменений. - Если пользователь принимает запрос на разрешение, изменения сохраняются обратно в исходный файл.
Это работает, но имеет некоторые ограничения. Эти изменения вносятся в видимую пользователю файловую систему, поэтому в ней существует множество проверок безопасности (например, безопасный просмотр в Chrome), которые предотвращают запись вредоносного контента в эту файловую систему. Эти записи выполняются не непосредственно, а с использованием временного файла. Исходный файл не изменяется, пока не пройдёт проверку всех проверок безопасности.
В результате эти операции довольно медленные. Это не так заметно при небольших изменениях текста, но производительность страдает при более значительных, масштабных обновлениях файлов, таких как изменения баз данных SQLite.
Как OPFS решает такие проблемы?
OPFS предлагает доступ к файлам на низком уровне, байт за байтом, который является частным для происхождения страницы и не виден пользователю. В результате он не требует той же серии проверок безопасности и предоставления разрешений, и поэтому быстрее, чем вызовы API доступа к файловой системе. Он также имеет набор синхронных вызовов (другие вызовы API файловой системы асинхронны), которые могут выполняться только внутри потоков Web Workers, чтобы не блокировать главный поток.
В качестве резюме, как OPFS отличается от видимой пользователем файловой системы:
- OPFS подчиняется ограничениям квот браузера на хранение, как и любой другой механизм хранения, разделенный по происхождению (например, API IndexedDB). Вы можете получить доступ к объёму дискового пространства, используемого OPFS, с помощью
navigator.storage.estimate(). - Очистка данных хранилища для сайта удаляет OPFS.
- Для доступа к файлам в OPFS не требуются запросы разрешения и проверки безопасности.
- Браузеры сохраняют содержимое OPFS на диске в каком-то месте, но вы не можете ожидать, что созданные файлы будут соответствовать друг другу один к одному. OPFS не предназначен для отображения пользователю.
Как получить доступ к OPFS?
Чтобы получить доступ к OPFS, вызывается метод navigator.storage.getDirectory(). Он возвращает ссылку на объект FileSystemDirectoryHandle, который представляет корень OPFS.
Работа с OPFS из основного потока
При доступе к OPFS из основного потока используются асинхронные API, основанные на Promise. Доступ к файлам (FileSystemFileHandle) и каталогам (FileSystemDirectoryHandle) осуществляется с помощью вызовов FileSystemDirectoryHandle.getFileHandle() и FileSystemDirectoryHandle.getDirectoryHandle() соответственно на объекте FileSystemDirectoryHandle, представляющем корень OPFS (и дочерние каталоги, при их создании).
Примечание: Передача { create: true } в вышеуказанные методы приводит к созданию файла или папки, если они не существуют.
// 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");
Чтение файла
- Вызовите
FileSystemDirectoryHandle.getFileHandle()для возврата объектаFileSystemFileHandle. - Вызовите метод
FileSystemFileHandle.getFile()для возврата объектаFile. Это специализированный типBlob, и, как таковой, с ним можно работать так же, как и с любым другимBlob. Например, вы можете получить прямой доступ к текстовому содержимому с помощьюBlob.text().
Запись файла
- Вызовите
FileSystemDirectoryHandle.getFileHandle()для возврата объектаFileSystemFileHandle. - Вызовите
FileSystemFileHandle.createWritable()для возврата объектаFileSystemWritableFileStream, который является специализированным типомWritableStream. - Запишите содержимое в него с помощью вызова
FileSystemWritableFileStream.write(). - Закройте поток с помощью
WritableStream.close().
Удаление файла или папки
Вы можете вызвать FileSystemDirectoryHandle.removeEntry() на родительском каталоге, передав ему имя элемента, который нужно удалить:
directoryHandle.removeEntry("my first nested file");
Также можно вызвать FileSystemHandle.remove() на объектах FileSystemFileHandle или FileSystemDirectoryHandle, представляющих элемент, который нужно удалить. Для удаления папки, включая все подпапки, передайте опцию { recursive: true }.
await fileHandle.remove();
await directoryHandle.remove({ recursive: true });
Следующее предоставляет быстрый способ очистить весь OPFS:
await (await navigator.storage.getDirectory()).remove({ recursive: true });
Перечисление содержимого папки
FileSystemDirectoryHandle является асинхронным итератором. Как таковой, вы можете перебирать его с помощью цикла for await...of и стандартных методов, таких как entries(), values() и keys().
Например:
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()) {
}
Работа с OPFS из потока Web Worker
Потоки Web Workers не блокируют главный поток, что означает, что в этом контексте вы можете использовать синхронные API доступа к файлам. Синхронные API быстрее, так как они не требуют работы с обещаниями.
Вы можете синхронно получить доступ к файлу, вызвав FileSystemFileHandle.createSyncAccessHandle() на обычном объекте FileSystemFileHandle:
Примечание: Несмотря на наличие «Sync» в названии, сам метод createSyncAccessHandle() является асинхронным.
const opfsRoot = await navigator.storage.getDirectory();
const fileHandle = await opfsRoot.getFileHandle("my-high-speed-file.txt", {
create: true,
});
const syncAccessHandle = await fileHandle.createSyncAccessHandle();
На возвращенном объекте FileSystemSyncAccessHandle доступно несколько синхронных методов:
-
getSize(): Возвращает размер файла в байтах. -
write(): Записывает содержимое буфера в файл, необязательно по заданному смещению, и возвращает количество записанных байтов. Проверка возвращенного количества записанных байтов позволяет вызывающим сторонам обнаруживать и обрабатывать ошибки и частичные записи. -
read(): Считывает содержимое файла в буфер, необязательно по заданному смещению. -
truncate(): Изменяет размер файла на заданный размер. -
flush(): Убеждается, что содержимое файла содержит все изменения, внесенные с помощьюwrite(). -
close(): Закрывает обработчик доступа.
Вот пример, использующий все упомянутые выше методы:
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, { at: 0 });
// 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);
См. также
- Система частных файлов происхождения на web.dev
© 2005–2024 MDN contributors.
Licensed under the Creative Commons Attribution-ShareAlike License v2.5 or later.
https://developer.mozilla.org/en-US/docs/Web/API/File_System_API/Origin_private_file_system