FileSystemFileHandle:createSyncAccessHandle() 方法
Baseline 2023
Newly available
Since March 2023, this feature works across the latest devices and browser versions. This feature might not work in older devices or browsers.
备注: 此特性仅在专用 Web Worker 中可用。
FileSystemFileHandle
接口的 createSyncAccessHandle()
方法返回一个 Promise
对象,可兑现一个用于同步读写文件的 FileSystemSyncAccessHandle
对象。此方法的同步特性带来了性能优势,但是只能在专用于操作源私有文件系统上的文件的 Web Worker 中使用。
创建 FileSystemSyncAccessHandle
会对与文件句柄关联的文件进行独占锁定。这用于在文件已有的访问句柄被关闭前,阻止对文件创建更多的 FileSystemSyncAccessHandle
或 FileSystemWritableFileStream
。
语法
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
-
如果在读写(
readwrite
)模式下句柄的PermissionStatus.state
不是granted
,则抛出该错误。 InvalidStateError
DOMException
-
如果
FileSystemSyncAccessHandle
对象代表的不是源私有文件系统上的文件,抛出此异常。 NotFoundError
DOMException
-
如果未找到当前条目,则抛出该异常。
NoModificationAllowedError
DOMException
-
如果浏览器无法获得文件句柄所关联的文件的锁定,抛出此异常。这可能是因为
mode
被设置为readwrite
,并尝试同时打开多个句柄。
示例
基本用法
以下异步事件处理函数处于 Web Worker 上下文。其中的代码片段创建了一个同步文件访问句柄。
onmessage = async (e) => {
// 获取从主线程发往 worker 的消息
const message = e.data;
// 获取草稿文件的句柄
const root = await navigator.storage.getDirectory();
const draftHandle = await root.getFileHandle("draft.txt", { create: true });
// 获取同步访问句柄
const accessHandle = await draftHandle.createSyncAccessHandle();
// ……
// 用完 FileSystemSyncAccessHandle 记得把它关闭
accessHandle.close();
};
带有 mode
选项的完整示例
我们的 createSyncAccessHandle()
模式测试示例提供了一个 <input>
字段来输入文本,以及两个按钮——一个用于将输入的文本写入原始私有文件系统中的文件末尾,另一个用于在文件太满时清空文件。
尝试探索上面的演示,打开浏览器开发者控制台,以便你可以看到正在发生的事情。如果你尝试在多个浏览器标签页中打开演示,你会发现可以同时打开多个句柄以同时写入文件。这是因为在 createSyncAccessHandle()
调用上设置了 mode: "readwrite-unsafe"
。
下面我们将探索代码。
HTML
两个 <button>
元素和文本 <input>
字段如下所示:
<ol>
<li>
<label for="filetext">输入要写入文件的文本:</label>
<input type="text" id="filetext" name="filetext" />
</li>
<li>将你的文本写入文件:<button class="write">写入文本</button></li>
<li>如果文件太满,则清空该文件:<button class="empty">清空文件</button></li>
</ol>
主线程 JavaScript
HTML 文件中的主线程 JavaScript 如下所示。我们获取对写入文本按钮、清空文件按钮和文本输入字段的引用,然后使用 Worker()
构造函数创建一个新的 Web Worker。然后我们定义两个函数并将它们设置为按钮上的事件处理器:
- 单击写入文本按钮时运行
writeToOPFS()
。此函数使用Worker.postMessage()
方法将文本字段的输入值发布到对象内的 Worker,然后清空文本字段,为下一次添加做好准备。请注意传递的对象还包含command: "write"
属性,以指定我们想要使用此消息触发写入操作。 - 单击清空文件按钮时运行
emptyOPFS()
。这会将包含command: "empty"
属性的对象发布到 Worker,指定要清空文件。
const writeBtn = document.querySelector(".write");
const emptyBtn = document.querySelector(".empty");
const fileText = document.querySelector("#filetext");
const opfsWorker = new Worker("worker.js");
function writeToOPFS() {
opfsWorker.postMessage({
command: "write",
content: fileText.value,
});
console.log("主线程脚本:发送给 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"
属性,允许多个句柄同时访问同一文件。
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 的 message 事件处理器中,我们首先使用 getSize()
获取文件的大小。然后,我们检查消息中发送的数据是否包含 command
属性值 "empty"
。如果是,我们使用 truncate()
清空文件,值为 0
,并更新 size
变量中包含的文件大小。
如果消息数据是其他内容,我们:
- 创建新的
TextEncoder
和TextDecoder
来处理稍后对文本内容的编码和解码。 - 使用
write()
对消息数据进行编码并将结果写入文件末尾,然后更新size
变量中包含的文件大小。 - 创建一个
DataView
来包含文件内容,并使用read()
将内容读入其中。 - 解码
DataView
内容并将其记录到控制台。
onmessage = function (e) {
console.log("Worker:从主线程收到消息");
// 获取文件当前大小
let size = accessHandle.getSize();
if (e.data.command === "empty") {
// 将文件截断为 0 字节
accessHandle.truncate(0);
// 获取文件当前大小
size = accessHandle.getSize();
} else {
const textEncoder = new TextEncoder();
const textDecoder = new TextDecoder();
// 对要写入文件的内容进行编码
const content = textEncoder.encode(e.data.content);
// 在文件末尾写入内容
accessHandle.write(content, { at: size });
// 获取文件当前大小
size = accessHandle.getSize();
// 准备文件长度的数据视图
const dataView = new DataView(new ArrayBuffer(size));
// 将整个文件读入数据视图
accessHandle.read(dataView, { at: 0 });
// 将当前文件内容记录到控制台
console.log("文件内容:" + textDecoder.decode(dataView));
// 刷新更改
accessHandle.flush();
}
// 将文件的大小记录到控制台
console.log("大小:" + size);
};
规范
Specification |
---|
File System Standard # api-filesystemfilehandle-createsyncaccesshandle |
浏览器兼容性
BCD tables only load in the browser