FileSystemFileHandle: createSyncAccessHandle() 方法

Baseline 已广泛支持

此功能已成熟,并可在许多设备和浏览器版本上运行。自 2023 年 3 月以来,它已在各种浏览器中可用。

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

注意:此功能仅在 专用 Web Worker 中可用。

FileSystemFileHandle 接口的 createSyncAccessHandle() 方法返回一个 Promise,该 Promise 解析为一个 FileSystemSyncAccessHandle 对象,可用于同步地读取和写入文件。此方法的同步性带来了性能优势,但它只能在专用 Web Worker 中用于 源私有文件系统 内的文件。

创建 FileSystemSyncAccessHandle 会对与文件句柄关联的文件进行独占锁定。这会阻止在现有访问句柄关闭之前为该文件创建进一步的 FileSystemSyncAccessHandleFileSystemWritableFileStream

语法

js
createSyncAccessHandle()
createSyncAccessHandle(options)

参数

options 可选

具有以下属性的对象:

mode 可选 非标准

一个字符串,指定访问句柄的锁定模式。默认值为 "readwrite"。可能的值为:

"read-only"

可以在文件上同时打开多个 FileSystemSyncAccessHandle 对象(例如,当在多个标签页中使用同一应用程序时),前提是它们都以 "read-only" 模式打开。一旦打开,就可以在句柄上调用类似读取的方法 — read()getSize()close()

"readwrite"

在一个文件上只能打开一个 FileSystemSyncAccessHandle 对象。尝试在第一个句柄关闭前打开后续句柄将导致抛出 NoModificationAllowedError 异常。一旦打开,就可以在句柄上调用任何可用方法。

"readwrite-unsafe"

可以在文件上同时打开多个 FileSystemSyncAccessHandle 对象,前提是它们都以 "readwrite-unsafe" 模式打开。一旦打开,就可以在句柄上调用任何可用方法。

返回值

一个 Promise,它解析为一个 FileSystemSyncAccessHandle 对象。

异常

NotAllowedError DOMException

如果句柄的 PermissionStatus.statereadwrite 模式下不是 granted,则抛出此异常。

InvalidStateError DOMException

如果 FileSystemSyncAccessHandle 对象不代表 源私有文件系统 中的文件,则抛出此异常。

NotFoundError DOMException

如果当前条目未找到,则抛出此异常。

NoModificationAllowedError DOMException

如果浏览器无法获取与文件句柄关联文件的锁定,则抛出此异常。这可能是因为 mode 设置为 readwrite,并且尝试同时打开多个句柄。

示例

基本用法

以下异步事件处理函数包含在 Web Worker 中。其中的代码片段会创建一个同步文件访问句柄。

js
onmessage = async (e) => {
  // Retrieve message sent to work from main script
  const message = e.data;

  // Get handle to draft file
  const root = await navigator.storage.getDirectory();
  const draftHandle = await root.getFileHandle("draft.txt", { create: true });
  // Get sync access handle
  const accessHandle = await draftHandle.createSyncAccessHandle();

  // …

  // Always close FileSystemSyncAccessHandle if done.
  accessHandle.close();
};

包含 mode 选项的完整示例

我们的 createSyncAccessHandle() 模式测试 示例(请参阅 源代码)提供了一个 <input> 字段用于输入文本,以及两个按钮 — 一个用于将输入的文本写入源私有文件系统的文件末尾,另一个用于在文件变得太满时清空文件。

尝试探索上面的演示,并打开浏览器开发者控制台,以便您可以看到正在发生的事情。如果您尝试在多个浏览器标签页中打开演示,您会发现可以同时打开多个句柄来写入文件。这是因为 createSyncAccessHandle() 调用上设置了 mode: "readwrite-unsafe"

下面我们将深入探讨代码。

HTML

两个 <button> 元素和文本 <input> 字段如下所示:

html
<ol>
  <li>
    <label for="file-text">Enter text to write to the file:</label>
    <input type="text" id="file-text" name="file-text" />
  </li>
  <li>
    Write your text to the file: <button class="write">Write text</button>
  </li>
  <li>
    Empty the file if it gets too full:
    <button class="empty">Empty file</button>
  </li>
</ol>

主 JavaScript

HTML 文件中的主线程 JavaScript 如下所示。我们获取写入文本按钮、清空文件按钮和文本输入字段的引用,然后使用 Worker() 构造函数创建一个新的 Web Worker。然后,我们定义两个函数并将它们设置为按钮的事件处理程序。

  • 当点击写入文本按钮时,会运行 writeToOPFS()。此函数使用 Worker.postMessage() 方法将文本字段的输入值作为对象发送到 Worker,然后清空文本字段,为下一次添加做好准备。请注意,传递的对象还包括一个 command: "write" 属性,用于指定我们要通过此消息触发写入操作。
  • 当点击清空文件按钮时,会运行 emptyOPFS()。此函数将包含 command: "empty" 属性的对象发送到 Worker,指定要清空文件。
js
const writeBtn = document.querySelector(".write");
const emptyBtn = document.querySelector(".empty");
const fileText = document.querySelector("#file-text");

const opfsWorker = new Worker("worker.js");

function writeToOPFS() {
  opfsWorker.postMessage({
    command: "write",
    content: fileText.value,
  });
  console.log("Main script: Text posted to worker");
  fileText.value = "";
}

function emptyOPFS() {
  opfsWorker.postMessage({
    command: "empty",
  });
}

writeBtn.addEventListener("click", writeToOPFS);
emptyBtn.addEventListener("click", emptyOPFS);

Worker JavaScript

Worker JavaScript 如下所示。

首先,我们运行一个名为 initOPFS() 的函数,该函数使用 StorageManager.getDirectory() 获取对 OPFS 根目录的引用,使用 FileSystemDirectoryHandle.getFileHandle() 创建一个文件并返回其句柄,然后使用 createSyncAccessHandle() 返回一个 FileSystemSyncAccessHandle。此调用包括 mode: "readwrite-unsafe" 属性,允许多个句柄同时访问同一文件。

js
let accessHandle;

async function initOPFS() {
  const opfsRoot = await navigator.storage.getDirectory();
  const fileHandle = await opfsRoot.getFileHandle("file.txt", { create: true });
  accessHandle = await fileHandle.createSyncAccessHandle({
    mode: "readwrite-unsafe",
  });
}

initOPFS();

在 Worker 的 消息事件 处理函数中,我们首先使用 getSize() 获取文件的大小。然后,我们检查发送到消息的数据是否包含 command 属性值 "empty"。如果是,我们使用 truncate() 并将值设置为 0 来清空文件,并更新 size 变量中包含的文件大小。

如果消息数据是其他内容,我们则

  • 创建一个新的 TextEncoderTextDecoder,以便稍后处理文本内容的编码和解码。
  • 编码消息数据,并使用 write() 将结果写入文件末尾,然后更新 size 变量中包含的文件大小。
  • 创建一个 DataView 来包含文件内容,并使用 read() 将内容读取到其中。
  • 解码 DataView 内容并将其记录到控制台。
js
onmessage = function (e) {
  console.log("Worker: Message received from main script");

  // Get the current size of the file
  let size = accessHandle.getSize();

  if (e.data.command === "empty") {
    // Truncate the file to 0 bytes
    accessHandle.truncate(0);

    // Get the current size of the file
    size = accessHandle.getSize();
  } else {
    const textEncoder = new TextEncoder();
    const textDecoder = new TextDecoder();

    // Encode content to write to the file
    const content = textEncoder.encode(e.data.content);
    // Write the content at the end of the file
    accessHandle.write(content, { at: size });

    // Get the current size of the file
    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 });

    // Log the current file contents to the console
    console.log(`File contents: ${textDecoder.decode(dataView)}`);

    // Flush the changes
    accessHandle.flush();
  }

  // Log the size of the file to the console
  console.log(`Size: ${size}`);
};

规范

规范
文件系统
# api-filesystemfilehandle-createsyncaccesshandle

浏览器兼容性

另见