Skip to content

Commit

Permalink
[zh-cn]: sync translation for File System API (#23425)
Browse files Browse the repository at this point in the history
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: A1lo <[email protected]>
  • Loading branch information
3 people authored Sep 8, 2024
1 parent 8a7c622 commit aa4bc7e
Show file tree
Hide file tree
Showing 18 changed files with 304 additions and 33 deletions.
4 changes: 2 additions & 2 deletions files/zh-cn/web/api/file_system_api/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
title: 文件系统 API
slug: Web/API/File_System_API
l10n:
sourceCommit: 2b6bddfe281c0179fbde9c870f9de7c0dc3829e8
sourceCommit: 0c3f18aca2c8a93d3982183f64bf7762c2c310b0
---

{{securecontext_header}}{{DefaultAPISidebar("File System API")}}{{AvailableInWorkers}}
Expand All @@ -20,7 +20,7 @@ l10n:
你还可以从以下途径获得句柄:

- {{domxref('HTML Drag and Drop API', 'HTML 拖放 API', '', 'nocode')}} 的 {{domxref('DataTransferItem.getAsFileSystemHandle()')}} 方法。
- [文件处理 API](https://developer.chrome.com/en/articles/file-handling/)
- [文件处理 API](https://developer.chrome.com/docs/capabilities/web-apis/file-handling)

每种句柄都提供了其独有的功能,取决于你使用的种类,会有些许差异(详见[接口](#接口)部分)。在获得句柄后,你便可以访问文件的数据或是被选中的目录的信息(包含子目录)。此 API 开辟了 web 此前一直缺乏的潜在功能。但不论如何,安全性是设计 API 时的首要考量,除非用户明确授权,否则就不允许访问文件和目录的数据(注意:[源私有文件系统](#源私有文件系统)并非如此,因为其对用户不可见)。

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
title: 源私有文件系统
slug: Web/API/File_System_API/Origin_private_file_system
l10n:
sourceCommit: 2b6bddfe281c0179fbde9c870f9de7c0dc3829e8
sourceCommit: 2cba64f68aab9e233fecfc2bab8bea4118716c14
---

{{securecontext_header}}{{DefaultAPISidebar("File System API")}}{{AvailableInWorkers}}
Expand Down Expand Up @@ -102,7 +102,7 @@ await (await navigator.storage.getDirectory()).remove({ recursive: true });

### 列出文件夹中的内容

{{domxref("FileSystemDirectoryHandle")}} 是一个[异步迭代器](/zh-CN/docs/Web/JavaScript/Reference/Iteration_protocols#异步迭代器和异步可迭代协议)。所以,你可以用 [`for awaitof`](/zh-CN/docs/Web/JavaScript/Reference/Statements/for-await...of) 循环和诸如 [`entries()`](/zh-CN/docs/Web/API/FileSystemDirectoryHandle/entries)[`values()`](/zh-CN/docs/Web/API/FileSystemDirectoryHandle/entries)[`keys()`](/zh-CN/docs/Web/API/FileSystemDirectoryHandle/entries) 这样的标准方法对其进行迭代。
{{domxref("FileSystemDirectoryHandle")}} 是一个[异步迭代器](/zh-CN/docs/Web/JavaScript/Reference/Iteration_protocols#异步迭代器和异步可迭代协议)。所以,你可以用 [`for await...of`](/zh-CN/docs/Web/JavaScript/Reference/Statements/for-await...of) 循环和诸如 [`entries()`](/zh-CN/docs/Web/API/FileSystemDirectoryHandle/entries)[`values()`](/zh-CN/docs/Web/API/FileSystemDirectoryHandle/entries)[`keys()`](/zh-CN/docs/Web/API/FileSystemDirectoryHandle/entries) 这样的标准方法对其进行迭代。

例如:

Expand Down Expand Up @@ -179,7 +179,7 @@ size = accessHandle.getSize();
const dataView = new DataView(new ArrayBuffer(size));

// 将整个文件读取到数据视图。
accessHandle.read(dataView);
accessHandle.read(dataView, { at: 0 });
// 打印 `"Some textMore content"`。
console.log(textDecoder.decode(dataView));

Expand Down
2 changes: 1 addition & 1 deletion files/zh-cn/web/api/filesystemdirectoryhandle/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
title: FileSystemDirectoryHandle
slug: Web/API/FileSystemDirectoryHandle
l10n:
sourceCommit: be3c45cd7a4d5c04139eceae10f7368251cdca64
sourceCommit: e92950d09467164afc9dfd8b35be9c909b63a8ab
---

{{securecontext_header}}{{APIRef("File System API")}}{{AvailableInWorkers}}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
title: FileSystemFileHandle:createSyncAccessHandle() 方法
slug: Web/API/FileSystemFileHandle/createSyncAccessHandle
l10n:
sourceCommit: f10fbe2d2dc4857bf29ce955689a7ba7c1ffac8b
sourceCommit: 2b6f99e45534ce662f842d8b4d2f7845492e353c
---

{{securecontext_header}}{{APIRef("File System API")}}{{AvailableInWorkers("dedicated")}}
Expand All @@ -15,11 +15,23 @@ l10n:

```js-nolint
createSyncAccessHandle()
createSyncAccessHandle(options)
```

### 参数

无。
- `options` {{optional_inline}}

- : 一个具有以下属性的对象:

- `mode` {{optional_inline}} {{non-standard_inline}}
- : 指定访问句柄的锁定模式的字符串。默认值为 `"readwrite"`。可能的值包括:
- `"read-only"`
- : 可以同时在一个文件上打开多个 `FileSystemSyncAccessHandle` 对象(例如,在多个标签页中使用同一个应用时),前提是它们都以 `"read-only"` 模式打开。打开后,可以在句柄上调用类似读取的方法——{{domxref("FileSystemSyncAccessHandle.read", "read()")}}、{{domxref("FileSystemSyncAccessHandle.getSize", "getSize()")}} 和 {{domxref("FileSystemSyncAccessHandle.close", "close()")}}。
- `"readwrite"`
- : 每个文件只能打开一个 `FileSystemSyncAccessHandle` 对象。如果在第一个句柄关闭之前尝试打开后续句柄,则会导致抛出 `NoModificationAllowedError` 异常。打开后,可以调用句柄上的任何可用方法。
- `"readwrite-unsafe"`
- : 可以同时在一个文件上打开多个 `FileSystemSyncAccessHandle` 对象,前提是它们都以 `"readwrite-unsafe"` 模式打开。打开后,可以在句柄上调用任何可用的方法。

### 返回值

Expand All @@ -34,10 +46,12 @@ createSyncAccessHandle()
- `NotFoundError` {{domxref("DOMException")}}
- : 如果未找到当前条目,则抛出该异常。
- `NoModificationAllowedError` {{domxref("DOMException")}}
- : 如果浏览器无法获得文件句柄所关联的文件的锁定,抛出此异常。
- : 如果浏览器无法获得文件句柄所关联的文件的锁定,抛出此异常。这可能是因为 `mode` 被设置为 `readwrite`,并尝试同时打开多个句柄。

## 示例

### 基本用法

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

```js
Expand All @@ -58,6 +72,134 @@ onmessage = async (e) => {
};
```

### 带有 `mode` 选项的完整示例

我们的 [`createSyncAccessHandle()` 模式测试](https://createsyncaccesshandle-mode-test.glitch.me/)示例提供了一个 {{htmlelement("input")}} 字段来输入文本,以及两个按钮——一个用于将输入的文本写入原始私有文件系统中的文件末尾,另一个用于在文件太满时清空文件。

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

下面我们将探索代码。

#### HTML

两个 {{htmlelement("button")}} 元素和文本 {{htmlelement("input")}} 字段如下所示:

```html
<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 如下所示。我们获取对写入文本按钮、清空文件按钮和文本输入字段的引用,然后使用 {{domxref("Worker.Worker", "Worker()")}} 构造函数创建一个新的 Web Worker。然后我们定义两个函数并将它们设置为按钮上的事件处理器:

- 单击写入文本按钮时运行 `writeToOPFS()`。此函数使用 {{domxref("Worker.postMessage()")}} 方法将文本字段的输入值发布到对象内的 Worker,然后清空文本字段,为下一次添加做好准备。请注意传递的对象还包含 `command: "write"` 属性,以指定我们想要使用此消息触发写入操作。
- 单击清空文件按钮时运行 `emptyOPFS()`。这会将包含 `command: "empty"` 属性的对象发布到 Worker,指定要清空文件。

```js
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()` 的函数,该函数使用 {{domxref("StorageManager.getDirectory()")}} 获取对 OPFS 根的引用,使用 {{domxref("FileSystemDirectoryHandle.getFileHandle()")}} 创建文件并返回其句柄,然后使用 `createSyncAccessHandle()` 返回 {{domxref("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 的 [message 事件](/zh-CN/docs/Web/API/Worker/message_event)处理器中,我们首先使用 {{domxref("FileSystemSyncAccessHandle.getSize", "getSize()")}} 获取文件的大小。然后,我们检查消息中发送的数据是否包含 `command` 属性值 `"empty"`。如果是,我们使用 {{domxref("FileSystemSyncAccessHandle.truncate", "truncate()")}} 清空文件,值为 `0`,并更新 `size` 变量中包含的文件大小。

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

- 创建新的 {{domxref("TextEncoder")}} 和 {{domxref("TextDecoder")}} 来处理稍后对文本内容的编码和解码。
- 使用 {{domxref("FileSystemSyncAccessHandle.write", "write()")}} 对消息数据进行编码并将结果写入文件末尾,然后更新 `size` 变量中包含的文件大小。
- 创建一个 {{jsxref("DataView")}} 来包含文件内容,并使用 {{domxref("FileSystemSyncAccessHandle.read", "read()")}} 将内容读入其中。
- 解码 `DataView` 内容并将其记录到控制台。

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

## 规范

{{Specifications}}
Expand Down
121 changes: 119 additions & 2 deletions files/zh-cn/web/api/filesystemfilehandle/createwritable/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
title: FileSystemFileHandle:createWritable() 方法
slug: Web/API/FileSystemFileHandle/createWritable
l10n:
sourceCommit: f10fbe2d2dc4857bf29ce955689a7ba7c1ffac8b
sourceCommit: 1a7695e13c51d85a81e3e5d85feedbc5dbd2a379
---

{{securecontext_header}}{{APIRef("File System API")}}{{AvailableInWorkers}}
Expand All @@ -26,6 +26,12 @@ createWritable(options)

- `keepExistingData` {{optional_inline}}
- : {{jsxref('Boolean', '布尔值', '', 'nocode')}},默认为 `false`。当设为 `true` 时,如果文件存在,则现将现有文件的内容复制到临时文件,否则临时文件初始时内容为空。
- `mode` {{optional_inline}} {{non-standard_inline}}
- : 指定可写文件流的锁定模式的字符串。默认值为 `"siloed"`。可能的值包括:
- `"exclusive"`
- : 只能打开一个 `FileSystemWritableFileStream` 写入器。在第一个写入器关闭之前尝试打开后续写入器会导致抛出 `NoModificationAllowedError` 异常。
- `"siloed"`
- : 可以同时打开多个 `FileSystemWritableFileStream` 写入器,每个写入器都有自己的交换文件,例如在多个标签页中使用同一个应用时。最后打开的写入器会写入其数据,因为每个写入器关闭时都会刷新数据。

### 返回值

Expand All @@ -38,12 +44,14 @@ createWritable(options)
- `NotFoundError` {{domxref("DOMException")}}
- : 如果未找到当前条目,则抛出该异常。
- `NoModificationAllowedError` {{domxref("DOMException")}}
- : 如果浏览器无法获取与文件句柄关联的文件的锁,则抛出该异常。
- : 如果浏览器无法获取与文件句柄关联的文件的锁,则抛出该异常。这可能是因为 `mode` 设置为 `exclusive`,并且尝试同时打开多个写入器。
- `AbortError` {{domxref("DOMException")}}
- : 如果实现定义的恶意软件扫描和安全浏览检查失败,则抛出此异常。

## 示例

### 基本用法

以下异步函数用于将给定内容写入文件句柄,从而写入磁盘。

```js
Expand All @@ -59,6 +67,115 @@ async function writeFile(fileHandle, contents) {
}
```

### 通过选项扩展用途

我们的 [`createWritable()` 模式测试](https://createwritable-mode-test.glitch.me/)示例提供了一个 {{htmlelement("button")}} 来选择要写入的文件,一个文本 {{htmlelement("input")}} 字段,你可以在其中输入一些要写入文件的文本,以及第二个 `<button>` 来将文本写入文件。

在上面的演示中,尝试选择文件系统上的文本文件(或输入新文件名),在输入字段中输入一些文本,然后将文本写入文件。打开文件系统上的文件以检查写入是否成功。

此外,尝试同时在两个浏览器标签页中打开页面。在第一个标签页中选择要写入的文件,然后立即尝试在第二个标签页中选择要写入的同一文件。你应该会收到一条错误消息,因为我们在 `createWritable()` 调用中设置了 `mode: "exclusive"`

下面我们将探索代码。

#### HTML

两个 {{htmlelement("button")}} 元素和文本 {{htmlelement("input")}} 字段如下所示:

```html
<ol>
<li>选择要写入的文件:<button class="select">选择文件</button></li>
<li>
<label for="filetext">输入要写入文件的文本:</label>
<input type="text" id="filetext" name="filetext" disabled />
</li>
<li>将你的文本写入文件:<button class="write" disabled>写入文本</button></li>
</ol>
```

文本输入字段和写入文本按钮最初通过 [`disabled`](/zh-CN/docs/Web/HTML/Attributes/disabled) 属性设置为禁用——直到用户选择要写入的文件时,它们才应被使用。

```css hidden
li {
margin-bottom: 10px;
}
```

#### JavaScript

我们首先获取对选择文件按钮、写入文本按钮和文本输入字段的引用。我们还声明一个全局变量 `writableStream`,它将存储对可写流的引用,用于在创建后将文本写入文件。我们最初将其设置为 `null`

```js
const selectBtn = document.querySelector(".select");
const writeBtn = document.querySelector(".write");
const fileText = document.querySelector("#filetext");

let writableStream = null;
```

接下来,我们创建一个名为 `selectFile()` 的异步函数,当按下选择按钮时,我们将调用该函数。它使用 {{domxref("Window.showSaveFilePicker()")}} 方法向用户显示文件选择器对话框,并为他们选择的文件创建文件句柄。在该句柄上,我们调用 `createWritable()` 方法来创建一个流,以将文本写入所选文件。如果调用失败,我们会将错误记录到控制台。

我们向 `createWritable()` 传递一个包含以下选项的选项对象:

- `keepExistingData: true`:如果所选文件已存在,则在开始写入之前,将其中包含的数据复制到临时文件中。

- `mode: "exclusive"`:表示只能同时在文件句柄上打开一个写入器。如果第二个用户加载示例并尝试选择文件,他们将收到错误。

最后,我们启用输入字段和写文本按钮,因为它们是下一步所需要的,并禁用选择文件按钮(目前不需要)。

```js
async function selectFile() {
// 创建新句柄
const handle = await window.showSaveFilePicker();

// 创建 FileSystemWritableFileStream 来写入
try {
writableStream = await handle.createWritable({
keepExistingData: true,
mode: "exclusive",
});
} catch (e) {
if (e.name === "NoModificationAllowedError") {
console.log(`你现在无法访问该文件;其他人正在尝试修改它。请稍后重试。`);
} else {
console.log(e.message);
}
}

// 启用文本字段和写入按钮,禁用选择按钮
fileText.disabled = false;
writeBtn.disabled = false;
selectBtn.disabled = true;
}
```

我们的下一个函数 `writeFile()` 使用 {{domxref("FileSystemWritableFileStream.write()")}} 将输入字段中输入的文本写入所选文件,然后清空输入字段。然后我们使用 {{domxref("WritableStream.close()")}} 关闭可写流,并重置演示以便再次运行——控件的 `disable` 状态切换回其原始状态,并且 `writableStream` 变量设置回 `null`

```js
async function writeFile() {
// 将文本写入我们的文件并清空文本字段
await writableStream.write(fileText.value);
fileText.value = "";

// 关闭文件并将内容写入磁盘。
await writableStream.close();

// 禁用文本字段和写入按钮,启用选择按钮
fileText.disabled = true;
writeBtn.disabled = true;
selectBtn.disabled = false;

// 将 writeableStream 重新设置为 null
writableStream = null;
}
```

为了使演示运行,我们在按钮上设置了事件监听器,以便在每个按钮被点击时运行相关的函数。

```js
selectBtn.addEventListener("click", selectFile);
writeBtn.addEventListener("click", writeFile);
```

## 规范

{{Specifications}}
Expand Down
2 changes: 1 addition & 1 deletion files/zh-cn/web/api/filesystemfilehandle/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
title: FileSystemFileHandle
slug: Web/API/FileSystemFileHandle
l10n:
sourceCommit: f10fbe2d2dc4857bf29ce955689a7ba7c1ffac8b
sourceCommit: d8f04d843dd81ab8cea1cfc0577ae3c5c9b77d5c
---

{{securecontext_header}}{{APIRef("File System API")}}{{AvailableInWorkers}}
Expand Down
Loading

0 comments on commit aa4bc7e

Please sign in to comment.