image.png
在体验 vscode 在线版时,发现在浏览器已经可以直接读写本地文件系统。查了一下发现是 File System Access API 提供了相关功能。

File System Access API 是一项 Web API,允许 Web 应用程序从用户设备的本地文件系统中读取和写入文件。

使用 File System Access API 可以访问本地文件系统,从而实现一些有用的功能,例如:

  • 将文件从本地文件系统上传到 Web 应用程序;
  • 将 Web 应用程序中的数据写入到本地文件系统中;
  • 在用户的本地文件系统上创建、重命名和删除文件;
  • 读取本地文件系统上的文件内容。

接口

FileSystemHandle

父 FileSystemHandle 类帮助定义两个子类:FileSystemFileHandle 和 FileSystemDirectoryHandle,分别用于文件和目录。

FileSystemFileHandle 操作文件

FileSystemFileHandle 对象是一个代表文件的对象,它提供了一些方法来获取和操作文件。例如:

实例方法

getFile 获取文件

返回一个 Promise 对象,用于获取文件;

createSyncAccessHandle

返回一个 FileSystemSyncAccessHandle 对象,用于同步访问文件;

createWritable 写入文件

返回一个Promise对象,用于创建一个可写流,用于写入文件;createWritable 方法会创建一个 FileSystemWritableFileStream 对象,然后通过它来写入文件。

  1. // 创建一个可写流
  2. const writable = await fileHandle.createWritable();
  3. // 写入数据
  4. await writable.write('Hello World!');
  5. // 关闭流
  6. await writable.close();

:::info 注意:操作文件流完成时,一定要记得关闭流,否则会导致文件锁定,无法进行其他操作 :::

属性

kind 属性和 name 属性,这两个属性是继承自FileSystemHandle对象的。

kind 类型 文件或目录

返回一个字符串,用于表示文件或目录; ‘directory’ | ‘file’

这里在 FileSystemFileHandle 文件对象中都是 ‘file’

name 文件或目录的名称

返回一个字符串,用于表示文件或目录的名称;

FileSystemDirectoryHandle 操作目录

FileSystemDirectoryHandle 对象是一个代表文件系统中的目录的对象,它提供了一些方法来获取和操作目录。

实例方法

  • entries:返回一个AsyncIterable对象,用于获取目录中的所有文件和目录;
  • keys:返回一个AsyncIterable对象,用于获取目录中的所有文件和目录的名称;
  • values:返回一个AsyncIterable对象,用于获取目录中的所有文件和目录的 FileSystemHandle 对象;
  • getFileHandle:返回一个Promise对象,用于获取目录中的文件;
  • getDirectoryHandle:返回一个Promise对象,用于获取目录中的目录;
  • removeEntry:返回一个Promise对象,用于删除目录中的文件或目录;
  • resolve:返回一个Promise对象,用于获取目录中的文件或目录;

entries、keys、values

这三个方法都是用来获取目录中的所有文件和目录的,它们返回的都是一个 AsyncIterable对象,我们可以通过for await…of 语法来遍历它。

  1. const directoryHandle = await window.showDirectoryPicker();
  2. for await (const [name, handle] of directoryHandle.entries()) {
  3. if (handle.kind === 'file') {
  4. console.log(name, 'file');
  5. } else {
  6. console.log(name, 'directory');
  7. }
  8. }

getFileHandle 、getDirectoryHandle

而这里的 getFileHandle、getDirectoryHandle 就是用来获取目录中的文件和目录的,它们都返回一个Promise对象,我们可以通过 await 来获取它们。

  1. const directoryHandle = await window.showDirectoryPicker();
  2. for await (const [name, handle] of directoryHandle.entries()) {
  3. if (handle.kind === 'file') {
  4. const fileHandle = await directoryHandle.getFileHandle(name);
  5. console.log(fileHandle);
  6. } else {
  7. const directoryHandle = await directoryHandle.getDirectoryHandle(name);
  8. console.log(directoryHandle);
  9. }
  10. }

getDirectoryHandle 创建目录

创建目录可以使用 FileSystemDirectoryHandle 对象的 getDirectoryHandle 方法。

  1. const directoryHandle = await window.showDirectoryPicker();
  2. const newDirectoryHandle = await directoryHandle.getDirect1oryHandle('new-directory', {
  3. create: true,
  4. });

getDirectoryHandle方法接收两个参数:

name:一个字符串,用于指定目录的名称;
options:一个对象,用于指定目录的选项(可选);
create:一个boolean值,默认为false,是否创建目录;
目前只有create一个选项,如果设置为true,则会创建一个目录,如果设置为false,则会获取一个目录。

如果目录不存在,且create为false,则会报错。

removeEntry 删除目录

删除目录可以使用FileSystemDirectoryHandle对象的removeEntry方法

  1. onst directoryHandle = await window.showDirectoryPicker();
  2. await directoryHandle.removeEntry('new-directory');

removeEntry方法接收一个参数,一个字符串,用于指定要删除的目录的名称。

属性

kind 属性和 name 属性,这两个属性是继承自FileSystemHandle对象的。

kind 类型 文件或目录

返回一个字符串,用于表示文件或目录; ‘directory’ | ‘file’

  1. const directoryHandle = await window.showDirectoryPicker();
  2. for await (const [name, handle] of directoryHandle.entries()) {
  3. if (handle.kind === 'file') {
  4. console.log(name, 'file');
  5. } else {
  6. console.log(name, 'directory');
  7. }
  8. }

name 文件或目录的名称

返回一个字符串,用于表示文件或目录的名称;

FileSystemSyncAccessHandle

FileSystemWritableFileStream

方法

window.showOpenFilePicker() 选择文件

showOpenFilePicker 方法会打开文件选择器,用户选择文件后会返回一个包含 FileHandle 对象的数组,我们可以通过它来获取文件的信息。

如果用户在未进行选择的情况下关闭提示,或者所选文件被认为过于敏感或危险而无法暴露给网站,则会引发 AbortError。

options 参数可选

包含选项的对象,如下所示:

multiple 是否支持多选

是否多选,boolean。默认为false,是否允许用户选择多个文件

excludeAcceptAllOption 是否支持所有类型

一个boolean值,默认为false,是否允许用户选择所有类型的文件

types 允许的文件类型

允许选取的文件类型的数组。每个项目都是具有以下选项的对象

description

一个字符串,用于描述文件类型(就是下拉选项的文字)

accept

一个对象,用于描述文件类型(就是控制选择文件的类型,例如image/*表示图片类型),具体可以参考:MIME types

  1. const fileHandle = await window.showOpenFilePicker({
  2. excludeAcceptAllOption: false,
  3. types: [
  4. {
  5. description: 'Text files',
  6. accept: {
  7. 'text/plain': ['.txt'],
  8. },
  9. },
  10. ],
  11. });

🙌🌰

showOpenFilePicker 是一个异步方法,它会返回一个 Promise 对象,我们可以通过 await 来等待它的结果。

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta http-equiv="X-UA-Compatible" content="IE=edge">
  6. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  7. <title>Document</title>
  8. </head>
  9. <body>
  10. <button onclick="getFile()"> getFile </button>
  11. <script>
  12. async function getFile() {
  13. const filePicked = await window.showOpenFilePicker();
  14. console.log("🚀 ~ file: index.html:17 ~ getFile ~ filePicked:", filePicked)
  15. const [fileHandle] = filePicked
  16. console.log("🚀 ~ file: index.html:18 ~ getFile ~ fileHandle:", fileHandle)
  17. const file = await fileHandle.getFile();
  18. console.log("🚀 ~ file: index.html:19 ~ getFile ~ file:", file)
  19. return file;
  20. }
  21. </script>
  22. </body>
  23. </html>

打印看看 showOpenFilePicker 最后返回的结果:
image.png
可以看到返回的结果是一个数组,这是因为我们可以选择多个文件;而这个数组的每一项都是一个FileSystemFileHandle 对象,我们可以通过它来获取和操作文件

window.showSaveFilePicker() 创建文件

showSaveFilePicker 也是文件选择器的一种,它和 showOpenFilePicker 的区别在于,showSaveFilePicker 是用来创建文件的,而 showOpenFilePicker 是用来选择文件的。

showSaveFilePicker 返回的是新创建的文件的 FileSystemFileHandle 对象,而 showOpenFilePicker 返回的是选择的文件的 FileSystemFileHandle 对象数组。

options 参数可选

excludeAcceptAllOption 是否支持所有类型

一个boolean值,默认为false,是否允许用户创建所有类型的文件

suggestedName 推荐名称

types 允许的文件类型

与 showOpenFilePicker 中的相同。

🙌🌰

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta http-equiv="X-UA-Compatible" content="IE=edge">
  6. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  7. <title>Document</title>
  8. </head>
  9. <body>
  10. <button onclick="saveFile()"> saveFile </button>
  11. <script>
  12. async function saveFile() {
  13. const fileHandle = await window.showSaveFilePicker({
  14. types: [
  15. {
  16. description: 'Text files',
  17. accept: {
  18. 'text/plain': ['.txt'],
  19. },
  20. },
  21. ],
  22. })
  23. console.log("🚀 ~ file: index.html:26 ~ saveFile ~ fileHandle:", fileHandle)
  24. // 创建一个可写流
  25. const writable = await fileHandle.createWritable();
  26. // 写入数据
  27. await writable.write('Hello World!');
  28. // 关闭流
  29. await writable.close();
  30. }
  31. </script>
  32. </body>
  33. </html>

window.showDirectoryFilePicker() 选择目录

showDirectoryFilePicker 方法会打开目录选择器,用户选择目录后返回 FileSystemDirectoryHandle 对象,我们可以通过它来获取目录的信息。

如果用户在未进行选择的情况下关闭提示,或者所选文件被认为过于敏感或危险而无法暴露给网站,则会引发 AbortError。
image.png

options 参数可选

包含选项的对象,如下所示:

id

通过指定 ID,浏览器可以记住不同 ID 的不同目录。如果对另一个选取器使用相同的 ID,则该选取器将在同一目录中打开。

mode

一个字符串,默认为只读访问,或是对目录的读写访问。”read” “readwrite”

startIn

🙌🌰

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta http-equiv="X-UA-Compatible" content="IE=edge">
  6. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  7. <title>Document</title>
  8. </head>
  9. <body>
  10. <button onclick="getDirectory()"> getDirectory </button>
  11. <script>
  12. async function getDirectory() {
  13. const directoryHandle = await window.showDirectoryPicker()
  14. console.log("🚀 ~ file: index.html:17 ~ getDirectory ~ directoryHandle:", directoryPicked)
  15. }
  16. </script>
  17. </body>
  18. </html>

打印看看 showDirectoryPicker 最后返回的结果:
image.png

可以看到,我们通过showDirectoryPicker方法获取到了目录,它返回的是一个FileSystemDirectoryHandle对象

使用 showDirectoryPicker 方法,浏览器会提示用户授权应用程序访问他们的文件系统,不要拒绝
image.png