File System API

安全上下文: 此功能仅在安全上下文(HTTPS)中可用,且支持此功能的浏览器数量有限。

注意:此功能在 Web Workers 中可用。

文件系统 API — 通过 文件系统访问 API 提供的扩展,允许访问设备文件系统上的文件 — 提供了读取、写入和文件管理功能。

请参阅 与其他文件相关 API 的关系,以了解此 API、文件和目录条目 API 以及 文件 API 之间的比较。

概念与用法

此 API 允许与用户本地设备上的文件或用户可访问的网络文件系统进行交互。此 API 的核心功能包括读取文件、写入或保存文件以及访问目录结构。

与文件和目录的大部分交互是通过句柄完成的。一个父级 FileSystemHandle 类有助于定义两个子类:FileSystemFileHandleFileSystemDirectoryHandle,分别代表文件和目录。

句柄代表用户系统上的文件或目录。您可以通过使用 window.showOpenFilePicker()window.showDirectoryPicker() 等方法向用户显示文件或目录选择器来首先获得对它们的访问。一旦调用这些方法,文件选择器就会出现,用户会选择一个文件或目录。一旦成功发生,就会返回一个句柄。

您也可以通过以下方式获得文件句柄的访问权限:

每个句柄都提供其自身的功能,并且根据您使用的句柄,会有一些差异(有关具体细节,请参阅接口部分)。然后,您可以访问文件数据,或所选目录的信息(包括其子项)。此 API 打开了 Web 一直以来所缺乏的潜在功能。尽管如此,在设计 API 时,安全性一直是重中之重,除非用户明确允许,否则不允许访问文件/目录数据(但这不适用于 源私有文件系统,因为它对用户不可见)。

注意:在 spec 中定义的相应页面上列出了使用此 API 功能时可能引发的不同异常。然而,API 与底层操作系统之间的交互使情况更加复杂。已提出一项 在 spec 中列出错误映射 的提案,其中包含有用的相关信息。

注意:基于 FileSystemHandle 的对象也可以被序列化成 IndexedDB 数据库实例,或者通过 postMessage() 传输。

源私有文件系统

源私有文件系统 (OPFS) 是作为文件系统 API 的一部分提供的存储端点,它对页面的源是私有的,并且不像常规文件系统那样对用户可见。它提供了对一种特殊类型文件的访问,该文件针对性能进行了高度优化,并提供了对其内容的就地写入访问。

以下是一些可能的用例:

  • 具有持久上传功能的应用程序

    • 当选择一个文件或目录进行上传时,您可以将文件复制到本地沙箱中,然后分块上传。
    • 应用程序可以在中断后重新启动上传,例如浏览器关闭或崩溃、连接中断或计算机关机。
  • 视频游戏或其他具有大量媒体资源的应用程序

    • 应用程序下载一个或多个大型 tar 包,并将它们本地解压到目录结构中。
    • 应用程序在后台预取资源,以便用户在不等待下载的情况下进行下一个任务或游戏关卡。
  • 具有离线访问或本地缓存的音频或照片编辑器(非常适合提高性能和速度)

    • 应用程序可以就地写入文件(例如,仅覆盖 ID3/EXIF 标签而不覆盖整个文件)。
  • 离线视频播放器

    • 应用程序可以下载大型文件(>1GB)以供以后观看。
    • 应用程序可以访问部分下载的文件(这样您就可以观看 DVD 的第一章,即使应用程序仍在下载其余内容,或者即使应用程序未能完成下载,因为您不得不匆忙赶火车)。
  • 离线 Web 邮件客户端

    • 客户端下载附件并将其本地存储。
    • 客户端缓存附件以供以后上传。

请阅读我们的 源私有文件系统 以获取如何使用它的说明。

保存文件

  • 对于异步句柄,请使用 FileSystemWritableFileStream 接口。一旦您想要保存的数据是 BlobString 对象、字符串字面量或 buffer 的格式,您就可以打开一个流并将数据保存到文件中。这可以是现有文件或新文件。
  • 对于同步 FileSystemSyncAccessHandle,您可以使用 write() 方法将更改写入文件。您还可以选择调用 flush(),如果您需要在特定时间将更改提交到磁盘(否则,您可以让底层操作系统自行处理,这在大多数情况下是可以的)。

接口

FileSystemChangeRecord 实验性

包含由 FileSystemObserver 观察到的单个更改的详细信息。

FileSystemHandle

表示文件系统条目的对象。多个句柄可以表示同一个条目。在大多数情况下,您不直接处理 FileSystemHandle,而是处理其子接口 FileSystemFileHandleFileSystemDirectoryHandle

FileSystemFileHandle

提供对文件系统条目的句柄。

FileSystemDirectoryHandle

提供对文件系统目录的句柄。

FileSystemObserver 实验性

提供一种观察选定文件或目录更改的机制。

FileSystemSyncAccessHandle

提供文件系统条目的同步句柄,该句柄直接在磁盘上的单个文件上进行操作。文件读写的同步性使得在异步操作具有高开销的上下文中(例如 WebAssembly)关键方法具有更高的性能。此类仅在 源私有文件系统 内的专用 Web Worker 中可访问。

FileSystemWritableFileStream

一个 WritableStream 对象,具有额外的便捷方法,直接在磁盘上的单个文件上操作。

其他接口的扩展

Window.showDirectoryPicker()

显示一个目录选择器,允许用户选择一个目录。

Window.showOpenFilePicker()

显示一个文件选择器,允许用户选择一个或多个文件。

Window.showSaveFilePicker()

显示一个文件选择器,允许用户保存一个文件。

DataTransferItem.getAsFileSystemHandle()

返回一个 Promise,如果拖动的项是文件,则以 FileSystemFileHandle 解析;如果拖动的项是目录,则以 FileSystemDirectoryHandle 解析。

StorageManager.getDirectory()

用于获取对 FileSystemDirectoryHandle 对象的引用,允许访问存储在 源私有文件系统 中的目录及其内容。返回一个 Promise,该 Promise 以 FileSystemDirectoryHandle 对象解析。

示例

访问文件

下面的代码允许用户从文件选择器中选择一个文件。

js
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() 方法检索内容。

js
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();
}

访问目录

以下示例返回一个具有指定名称的目录句柄。如果目录不存在,则会创建它。

js
const dirName = "directoryToGetName";

// assuming we have a directory handle: 'currentDirHandle'
const subDir = await currentDirHandle.getDirectoryHandle(dirName, {
  create: true,
});

以下异步函数使用 resolve() 方法查找相对于指定目录句柄的选定文件的路径。

js
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 然后被写入流,随后流被关闭。

js
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() 方法的选项的不同示例。

js
// 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 来存储它。
  • 将文件内容读入缓冲区。
  • 对消息进行编码并将其写入文件末尾。
  • 将更改持久化到磁盘并关闭访问句柄。
js
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();
};

注意:在 spec 的早期版本中,close()flush()getSize()truncate() 被不方便地指定为异步方法。这已经 修正,但一些浏览器仍然支持异步版本。

规范

规范
文件系统
文件系统访问

浏览器兼容性

api.FileSystemHandle

api.FileSystemFileHandle

api.FileSystemDirectoryHandle

api.FileSystemWritableFileStream

api.FileSystemSyncAccessHandle

另见