文件系统 API
注意:此功能在 Web Workers 中可用。
文件系统 API — 通过 文件系统访问 API 提供的扩展来访问设备文件系统上的文件 — 允许读取、写入和文件管理功能。
概念和用法
此 API 允许与用户本地设备或用户可访问的网络文件系统上的文件交互。此 API 的核心功能包括读取文件、写入或保存文件以及访问目录结构。
大多数与文件和目录的交互都是通过句柄完成的。父 FileSystemHandle
类有助于定义两个子类:FileSystemFileHandle
和 FileSystemDirectoryHandle
,分别用于文件和目录。
句柄表示用户系统上的文件或目录。你可以首先使用 window.showOpenFilePicker()
和 window.showDirectoryPicker()
等方法向用户显示文件或目录选择器来获取访问权限。调用这些方法后,文件选择器将显示出来,用户可以选择文件或目录。如果成功,则返回一个句柄。
你也可以通过以下方式获取文件句柄访问权限:
每个句柄都提供自己的功能,并且根据你使用的是哪个句柄,存在一些差异(有关具体细节,请参阅 接口 部分)。然后,你可以访问文件数据或所选目录的信息(包括子目录)。此 API 开放了 Web 一直缺乏的潜在功能。然而,在设计 API 时,安全性一直是首要考虑因素,除非用户明确允许,否则不允许访问文件/目录数据(请注意,源代码专用文件系统 并非如此,因为它对用户不可见)。
注意:使用此 API 功能时可能引发的不同异常在规范中相关页面的定义中列出。但是,API 与底层操作系统的交互会使情况更加复杂。已经提出了一份建议,在规范中列出错误映射,其中包括有用的相关信息。
注意:基于 FileSystemHandle
的对象也可以序列化到 IndexedDB 数据库实例中,或者通过 postMessage()
传输。
源代码专用文件系统
源代码专用文件系统 (OPFS) 是作为文件系统 API 的一部分提供的存储端点,该端点对页面的源代码专用,对用户不可见,就像常规文件系统一样。它提供对一种特殊文件的访问权限,这种文件针对性能进行了高度优化,并提供对内容的原地写入访问权限。
阅读我们的 源代码专用文件系统,了解如何使用它。
保存文件
- 对于异步句柄,请使用
FileSystemWritableFileStream
接口。一旦你想要保存的数据以Blob
、String
对象、字符串文字或buffer
的格式存在,你就可以打开一个流并将数据保存到文件中。这可以是现有文件或新文件。 - 对于同步
FileSystemSyncAccessHandle
,你可以使用write()
方法将更改写入文件。你也可以选择调用flush()
,如果你需要在特定时间将更改提交到磁盘(否则,你可以让底层操作系统在适当的时候处理它,这在大多数情况下应该没问题)。
接口
FileSystemHandle
-
表示文件或目录条目的对象。多个句柄可以表示相同的条目。在大多数情况下,你不会直接使用
FileSystemHandle
,而是使用它的子接口FileSystemFileHandle
和FileSystemDirectoryHandle
。 FileSystemFileHandle
-
提供对文件系统条目的句柄。
FileSystemDirectoryHandle
-
提供对文件系统目录的句柄。
FileSystemSyncAccessHandle
-
提供对文件系统条目的同步句柄,它在磁盘上的单个文件上就地操作。文件读取和写入的同步性质允许在异步操作开销较高的上下文中(例如,WebAssembly)对关键方法进行更高性能的操作。此类仅在专用的 Web Workers 中对 源代码专用文件系统 中的文件可用。
FileSystemWritableFileStream
-
一个带有额外便利方法的
WritableStream
对象,它对磁盘上的单个文件进行操作。
对其他接口的扩展
Window.showDirectoryPicker()
-
显示一个目录选择器,允许用户选择一个目录。
Window.showOpenFilePicker()
-
显示一个文件选择器,允许用户选择一个或多个文件。
Window.showSaveFilePicker()
-
显示一个文件选择器,允许用户保存一个文件。
DataTransferItem.getAsFileSystemHandle()
-
如果拖动的项目是文件,则返回
FileSystemFileHandle
;如果拖动的项目是目录,则返回FileSystemDirectoryHandle
。 StorageManager.getDirectory()
-
用于获取对
FileSystemDirectoryHandle
对象的引用,该对象允许访问存储在 源代码专用文件系统 中的目录及其内容。返回一个Promise
,它使用FileSystemDirectoryHandle
对象来满足。
示例
访问文件
以下代码允许用户从文件选择器中选择一个文件。
async function getFile() {
// Open file picker and destructure the result the first handle
const [fileHandle] = await window.showOpenFilePicker();
const file = await fileHandle.getFile();
return file;
}
以下异步函数显示一个文件选择器,一旦选择了文件,就会使用 getFile()
方法检索内容。
const pickerOpts = {
types: [
{
description: "Images",
accept: {
"image/*": [".png", ".gif", ".jpeg", ".jpg"],
},
},
],
excludeAcceptAllOption: true,
multiple: false,
};
async function getTheFile() {
// Open file picker and destructure the result the first handle
const [fileHandle] = await window.showOpenFilePicker(pickerOpts);
// get file contents
const fileData = await fileHandle.getFile();
}
访问目录
以下示例返回具有指定名称的目录句柄。如果目录不存在,则创建它。
const dirName = "directoryToGetName";
// assuming we have a directory handle: 'currentDirHandle'
const subDir = currentDirHandle.getDirectoryHandle(dirName, { create: true });
以下异步函数使用 resolve()
查找所选文件的路径,该路径相对于指定的目录句柄。
async function returnPathDirectories(directoryHandle) {
// Get a file handle by showing a file picker:
const [handle] = await self.showOpenFilePicker();
if (!handle) {
// User cancelled, or otherwise failed to open a file.
return;
}
// Check if handle exists inside our directory handle
const relativePaths = await directoryHandle.resolve(handle);
if (relativePaths === null) {
// Not inside directory handle
} else {
// relativePaths is an array of names, giving the relative path
for (const name of relativePaths) {
// log each entry
console.log(name);
}
}
}
写入文件
以下异步函数打开保存文件选择器,该选择器会在选择文件后返回一个 FileSystemFileHandle
。然后,使用 FileSystemFileHandle.createWritable()
方法创建一个可写流。
然后,将用户定义的 Blob
写入流,随后关闭流。
async function saveFile() {
// create a new handle
const newHandle = await window.showSaveFilePicker();
// create a FileSystemWritableFileStream to write to
const writableStream = await newHandle.createWritable();
// write our file
await writableStream.write(imgBlob);
// close the file and write the contents to disk.
await writableStream.close();
}
以下显示了可以传递到 write()
方法中的不同选项示例。
// just pass in the data (no options)
writableStream.write(data);
// writes the data to the stream from the determined position
writableStream.write({ type: "write", position, data });
// updates the current file cursor offset to the position specified
writableStream.write({ type: "seek", position });
// resizes the file to be size bytes long
writableStream.write({ type: "truncate", size });
在 OPFS 中同步读取和写入文件
此示例同步地将文件读取到 源代码专用文件系统 中,并写入该系统。
以下异步事件处理程序函数包含在 Web Worker 中。在收到来自主线程的消息时,它会:
- 创建一个同步文件访问句柄。
- 获取文件的大小并创建一个
ArrayBuffer
来容纳它。 - 将文件内容读取到缓冲区中。
- 对消息进行编码并将其写入文件的末尾。
- 将更改持久保存到磁盘并关闭访问句柄。
onmessage = async (e) => {
// retrieve message sent to work from main script
const message = e.data;
// Get handle to draft file in OPFS
const root = await navigator.storage.getDirectory();
const draftHandle = await root.getFileHandle("draft.txt", { create: true });
// Get sync access handle
const accessHandle = await draftHandle.createSyncAccessHandle();
// Get size of the file.
const fileSize = accessHandle.getSize();
// Read file content to a buffer.
const buffer = new DataView(new ArrayBuffer(fileSize));
const readBuffer = accessHandle.read(buffer, { at: 0 });
// Write the message to the end of the file.
const encoder = new TextEncoder();
const encodedMessage = encoder.encode(message);
const writeBuffer = accessHandle.write(encodedMessage, { at: readBuffer });
// Persist changes to disk.
accessHandle.flush();
// Always close FileSystemSyncAccessHandle if done.
accessHandle.close();
};
注意:在规范的早期版本中,close()
、flush()
、getSize()
和 truncate()
非人性化地被指定为异步方法。现在已经 进行了修正,但一些浏览器仍然支持异步版本。
规范
规范 |
---|
文件系统标准 |
文件系统访问 |
浏览器兼容性
api.FileSystemHandle
BCD 表格仅在浏览器中加载
api.FileSystemFileHandle
BCD 表格仅在浏览器中加载
api.FileSystemDirectoryHandle
BCD 表格仅在浏览器中加载
api.FileSystemWritableFileStream
BCD 表格仅在浏览器中加载
api.FileSystemSyncAccessHandle
BCD 表格仅在浏览器中加载