文件系统访问 API 入门

Avatar of Charlie Gerard
Charlie Gerard

DigitalOcean 为您旅程的每个阶段提供云产品。开始使用 200 美元的免费额度!

文件系统访问 API 是一个 Web API,允许读取和写入用户本地文件。它开启了构建强大 Web 应用程序的新功能,例如文本编辑器或 IDE、图像编辑工具、改进的导入/导出,所有这些都在前端进行。让我们了解一下如何开始使用此 API。

Screenshot of an alert popup asking the user if they want the site to be able to view their files via the File System Access API.

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

在深入研究从用户系统读取文件所需的代码之前,请记住一个重要细节,即 **调用文件系统访问 API 必须由用户手势在安全上下文中执行**。在以下示例中,我们将使用单击事件。

从单个文件读取

从文件读取数据可以在不到 10 行代码中完成。这是一个示例代码片段

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 中有一个带有类 .pick-file 的按钮。单击此按钮时,我们通过调用 window.showOpenFilePicker() 启动文件选择器,并将此查询的结果存储在一个名为 fileHandle 的变量中。

调用 showOpenFilePicker() 返回的是一个 FileSystemFileHandle 对象数组,表示我们选择的每个文件。由于此示例用于单个文件,因此我们解构结果。我稍后将展示如何选择多个文件。

这些对象包含一个 kindname 属性。如果您要使用 console.log(fileHandle),您将看到以下对象

FileSystemFileHandle {kind: 'file', name: 'data.txt'}

kind 可以是 filedirectory

然后,我们可以在 fileHandle 上调用 getFile() 方法以获取有关我们文件的信息。调用此方法会返回一个包含一些属性的对象,包括文件上次修改的时间戳、文件名、大小和类型。

最后,我们可以对文件调用 text() 以获取其内容。

从多个文件读取

要从多个文件读取,我们需要将一个 options 对象传递给 showOpenFilePicker()

例如

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 对象将包含以下内容

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

在此示例中,fileHandles 是一个包含多个文件的数组,因此获取其内容将按以下方式完成

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 还允许您将内容写入文件。首先,让我们了解一下如何保存新文件。

写入新文件

写入新文件也可以用非常少的代码完成!

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;
};

如果我们想象一个带有类 save-file 的第二个按钮,单击时,我们将使用 showSaveFilePicker() 方法打开文件选择器,并传入一个包含要保存的文件类型的 option 对象,这里是一个 .txt 文件。

调用此方法也将像第一部分一样返回一个 FileSystemFileHandle 对象。在此对象上,我们可以调用 createWritable() 方法,该方法将返回一个 FileSystemWritableFileStream 对象。然后,我们可以使用 write() 方法将一些内容写入此流,在其中我们需要传递内容。

最后,我们需要调用 close() 方法关闭文件并完成将内容写入磁盘。

例如,如果您想将一些 HTML 代码写入文件,则只需要更改 options 对象中的内容以接受 "text/html": [".html"] 并将一些 HTML 内容传递给 write() 方法即可。

编辑现有文件

如果您想导入文件并使用文件系统访问 API 编辑它,则示例代码如下所示

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 还允许您列出目录中的文件以及删除文件或目录。

读取目录

读取目录可以使用少量代码完成

document.querySelector(".read-dir").onclick = async () => {
 const directoryHandle = await window.showDirectoryPicker();
 
 for await (const entry of directoryHandle.values()) {
   console.log(entry.kind, entry.name);
 }
};

如果我们添加一个带有类 .read-dir 的新按钮,单击时,调用 showDirectoryPicker() 方法将打开文件选择器,并且当您在计算机上选择一个目录时,此代码将列出在该目录中找到的文件。

删除文件

可以使用以下代码示例删除目录中的文件

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

如果您想删除文件夹,则只需要对上面的代码示例进行少量更改

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

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

// 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");
};

如果您想删除整个文件夹,则需要以下几行代码

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

文件系统访问 API 浏览器支持

目前,IE 和 Firefox 似乎不支持文件系统访问 API。但是,存在一个名为 browser-fs-accessponyfill

此浏览器支持数据来自 Caniuse,其中包含更多详细信息。数字表示浏览器在该版本及更高版本中支持该功能。

桌面

ChromeFirefoxIEEdgeSafari
130132不支持127试验

移动/平板电脑

Android ChromeAndroid FirefoxAndroidiOS Safari
127不支持12718.0

总结

如果您想尝试文件系统访问 API,请查看由 Google 工程师构建的此实时演示文本编辑器。否则,如果您想了解有关此 API 及其所有功能的更多信息,以下是一些资源