File System Access API

文件系统访问 API 是一种网页 API, 允许访问和修改用户本地的文件。它为构建强大的网页应用打开了新的可能,例如文本编辑器、IDE、图像处理软件,也可以轻松实现导入与导出功能,所有这些都可以在网页前端实现。下面让我们一起来看看如何开始使用这个 API。

使用文件系统访问 API 读取文件

在我们深入研究从用户系统中读取文件的代码之前,有一个很重要的细节需要牢记:调用文件系统访问 API 必须通过用户操作触发,并在安全的上下文中进行。 在下面的例子中,我们将在点击事件中调用该 API。

读取单个文件

从一个文件中读取数据,代码不到 10 行就可以实现。下面是一个示例:

ini 复制代码
 let fileHandle;

 document.querySelector(".pick-file").onclick = async () => {
  [fileHandle] = await window.showOpenFilePicker();

  const file = await fileHandle.getFile();
  const content = await file.text();

  return content;
 };

假设我们在 HTML 中有一个 class 为 .pick-file 的按钮。当点击这个按钮时,我们通过调用 window.showOpenFilePicker() 启动文件选择器,并将返回结果存储在一个名为 fileHandle 的变量中。

调用 showOpenFilePicker() 返回的是表示所选文件的 FileSystemFileHandle 对象数组。由于这个例子只选择单个文件,我们会解构这个结果。关于选择多个文件我稍后会介绍。

这些对象包含 kind 和 name 属性。如果打印 console.log(fileHandle), 你会看到如下对象:

css 复制代码
 FileSystemFileHandle {kind: 'file', name: 'data.txt'}

kind 属性的值可以是 file 或者 directory。

在 fileHandle 对象上,我们可以调用 getFile () 方法来获取文件详情。调用这个方法会返回一个对象,包含文件最后修改时间、文件名、大小和类型等属性。

最后,我们可以在文件对象上调用 text () 方法获取文件内容。

读取多个文件

如果要读取多个文件,我们需要向 showOpenFilePicker () 传入一个 options 对象。

例如:

dart 复制代码
 let fileHandles;
 const options = {
  multiple: true,
 };

 document.querySelector(".pick-file").onclick = async () => {
  fileHandles = await window.showOpenFilePicker(options);

  // The rest of the code will be shown below
 };

默认情况下,multiple 属性为 false。我们还可以通过选项指明可选择的文件类型。

例如,如果只希望选择 .jpeg 文件,options 对象会包含以下内容:

css 复制代码
 const options = {
  types: [
    {
      description: "Images",
      accept: {
        "image/jpeg": ".jpeg",
      },
    },
  ],
  excludeAcceptAllOption: true,
 };

在这个例子中,fileHandles 是一个包含多个文件的数组,获取内容的方法如下:

ini 复制代码
 let fileHandles;
 const options = {
  multiple: true,
 };

 document.querySelector(".pick-file").onclick = async () => {
  fileHandles = await window.showOpenFilePicker(options);

  const allContent = await Promise.all(
    fileHandles.map(async (fileHandle) => {
      const file = await fileHandle.getFile();
      const content = await file.text();
      return content;
    })
  );

  console.log(allContent);
 };

使用文件系统访问 API 写入文件

文件系统访问 API 也允许您向文件写入内容。首先,让我们看一下如何保存新文件。

写入新文件

向新文件写入也可以用很少的代码实现!

dart 复制代码
 document.querySelector(".save-file").onclick = async () => {
  const options = {
    types: [
      {
        description: "Test files",
        accept: {
          "text/plain": [".txt"],
        },
      },
    ],
  };

  const handle = await window.showSaveFilePicker(options);
  const writable = await handle.createWritable();

  await writable.write("Hello World");
  await writable.close();

  return handle;
 };

假设我们有第二个 class 为 save-file 的按钮,点击时,通过 showSaveFilePicker() 方法打开文件选择器,并在 options 对象中指明要保存的文件类型,这里是 .txt 文件。

调用这个方法同样会返回一个 FileSystemFileHandle 对象,就像第一部分一样。在这个对象上调用 createWritable() 方法会返回一个 FileSystemWritableFileStream 对象。然后我们可以通过 write() 方法往这个流里写入内容。

最后,需要调用 close() 方法来关闭文件,完成内容写入。

例如,如果要写入 HTML 代码到文件,只需要在 options 对象里将内容类型设为 "text/html" , 调用 write () 方法传入 HTML 内容即可。

编辑现有文件

如果要导入一个文件并用文件系统访问 API 编辑,示例代码如下:

ini 复制代码
 let fileHandle;

 document.querySelector(".pick-file").onclick = async () => {
  [fileHandle] = await window.showOpenFilePicker();

  const file = await fileHandle.getFile();
  const writable = await fileHandle.createWritable();

  await writable.write("This is a new line");
  await writable.close();
 };

如果你已经阅读过前面的内容,可能会注意到我们首先通过 showOpenFilePicker()getFile() 方法读取一个文件,然后用 createWritable()write()close() 方法写入同一个文件。

如果导入的文件已经包含一些内容,这个代码会用传给 write() 方法的新内容替换原有的文件内容。

文件系统访问 API 的其他功能

这里不详细展开,文件系统访问 API 还可以列出目录中的文件,以及删除文件和目录。

读取目录

读取目录只需要很少的代码:

dart 复制代码
 document.querySelector(".read-dir").onclick = async () => {
  const directoryHandle = await window.showDirectoryPicker();

  for await (const entry of directoryHandle.values()) {
    console.log(entry.kind, entry.name);
  }
 };

如果我们添加一个新的 class 为 .read-dir 的按钮,点击时调用 showDirectoryPicker () 方法会打开文件选择器,选择计算机上的一个目录后,这段代码会列出该目录中的文件。

删除文件

可以用以下代码删除一个目录中的文件:

dart 复制代码
 document.querySelector(".pick-file").onclick = async () => {
  const [fileHandle] = await window.showOpenFilePicker();
  await fileHandle.remove();
 };

如果您想删除一个文件夹,只需对上述代码示例做一个小修改即可:

dart 复制代码
 document.querySelector(".read-dir").onclick = async () => {
  const directoryHandle = await window.showDirectoryPicker();
  await directoryHandle.remove();
 };

最后,如果您在选择文件夹时想要删除一个特定文件,可以这样写:

dart 复制代码
 // Delete a single file named data.txt in the selected folder
 document.querySelector(".pick-folder").onclick = async () => {
    const directoryHandle = await window.showDirectoryPicker();
    await directoryHandle.removeEntry("data.txt");
 };

如果想删除整个文件夹,需要以下代码:

dart 复制代码
 // Recursively delete the folder named "data"
 document.querySelector(".pick-folder").onclick = async () => {
    const directoryHandle = await window.showDirectoryPicker();
    await directoryHandle.removeEntry('data', { recursive: true });
 };

File System Access API 浏览器支持情况

目前,IE 和 Firefox 浏览器似乎都不支持 File System Access API。不过,存在一个名为 browser-fs-access 的腻子 (polyfill)。

总结

如果你想要尝试 File System Access API, 可以查看谷歌工程师开发的这个在线演示文本编辑器。另外,如果你想学习更多关于这个 API 及其所有功能的信息,这里有一些资源:

  • File System Access API (W3C 规范)
  • File System Access API (MDN)
  • 对比度范围,replaceAll 方法,原生文件系统 API (Šime Vidas)
  • File System Access API: 简化访问本地文件的操作 (web.dev)
  • 使用 browser-fs-access 库读取和写入文件与目录 (web.dev)
  • browser-fs-access 仓库 (GitHub)
相关推荐
EamonYang几秒前
实现 miniReact
前端
前端日常开发1 分钟前
5 分钟搞懂JavaScript 原型链
前端
野生的程序媛2 分钟前
重生之我在学Vue--第7天 Vue 3 数据请求(Axios)
前端·javascript·vue.js
cv高级工程师YKY3 分钟前
前端 - uniapp - - 滚动容器scroll-view实现横向滚动
前端·uni-app
程序媛刘刘6 分钟前
uniappx 使用体验
java·服务器·前端
coder阿龙10 分钟前
【UNIAPP】获取视频的第一帧作为封面(基于视频URL,Canvas)复制即用
前端·uni-app·音视频
蘑菇王11 分钟前
无需打包构建?ESM Bundleless 开发的探索与实践
前端·javascript
只会写Bug的程序员15 分钟前
面试之《TypeScript泛型》
前端·面试·typescript
宇寒风暖26 分钟前
HTML嵌入CSS样式超详解(尊享)
前端·css·笔记·学习·html
秋天的一阵风30 分钟前
‌ES Module 都过十岁生日了,你还不了解它的运行原理吗?
前端·javascript·面试