在体验 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 获取文件
createSyncAccessHandle
返回一个 FileSystemSyncAccessHandle 对象,用于同步访问文件;
createWritable 写入文件
返回一个Promise对象,用于创建一个可写流,用于写入文件;createWritable
方法会创建一个 FileSystemWritableFileStream
对象,然后通过它来写入文件。
// 创建一个可写流
const writable = await fileHandle.createWritable();
// 写入数据
await writable.write('Hello World!');
// 关闭流
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 语法来遍历它。
const directoryHandle = await window.showDirectoryPicker();
for await (const [name, handle] of directoryHandle.entries()) {
if (handle.kind === 'file') {
console.log(name, 'file');
} else {
console.log(name, 'directory');
}
}
getFileHandle 、getDirectoryHandle
而这里的 getFileHandle、getDirectoryHandle 就是用来获取目录中的文件和目录的,它们都返回一个Promise对象,我们可以通过 await 来获取它们。
const directoryHandle = await window.showDirectoryPicker();
for await (const [name, handle] of directoryHandle.entries()) {
if (handle.kind === 'file') {
const fileHandle = await directoryHandle.getFileHandle(name);
console.log(fileHandle);
} else {
const directoryHandle = await directoryHandle.getDirectoryHandle(name);
console.log(directoryHandle);
}
}
getDirectoryHandle 创建目录
创建目录可以使用 FileSystemDirectoryHandle 对象的 getDirectoryHandle 方法。
const directoryHandle = await window.showDirectoryPicker();
const newDirectoryHandle = await directoryHandle.getDirect1oryHandle('new-directory', {
create: true,
});
getDirectoryHandle方法接收两个参数:
name:一个字符串,用于指定目录的名称;
options:一个对象,用于指定目录的选项(可选);
create:一个boolean值,默认为false,是否创建目录;
目前只有create一个选项,如果设置为true,则会创建一个目录,如果设置为false,则会获取一个目录。
如果目录不存在,且create为false,则会报错。
removeEntry 删除目录
删除目录可以使用FileSystemDirectoryHandle
对象的removeEntry
方法
onst directoryHandle = await window.showDirectoryPicker();
await directoryHandle.removeEntry('new-directory');
removeEntry方法接收一个参数,一个字符串,用于指定要删除的目录的名称。
属性
kind 属性和 name 属性,这两个属性是继承自FileSystemHandle对象的。
kind 类型 文件或目录
返回一个字符串,用于表示文件或目录; ‘directory’ | ‘file’
const directoryHandle = await window.showDirectoryPicker();
for await (const [name, handle] of directoryHandle.entries()) {
if (handle.kind === 'file') {
console.log(name, 'file');
} else {
console.log(name, 'directory');
}
}
name 文件或目录的名称
FileSystemSyncAccessHandle
FileSystemWritableFileStream
方法
window.showOpenFilePicker() 选择文件
showOpenFilePicker 方法会打开文件选择器,用户选择文件后会返回一个包含 FileHandle
对象的数组,我们可以通过它来获取文件的信息。
如果用户在未进行选择的情况下关闭提示,或者所选文件被认为过于敏感或危险而无法暴露给网站,则会引发 AbortError。
options 参数可选
包含选项的对象,如下所示:
multiple 是否支持多选
是否多选,boolean。默认为false,是否允许用户选择多个文件
excludeAcceptAllOption 是否支持所有类型
一个boolean值,默认为false,是否允许用户选择所有类型的文件
types 允许的文件类型
description
accept
一个对象,用于描述文件类型(就是控制选择文件的类型,例如image/*表示图片类型),具体可以参考:MIME types
const fileHandle = await window.showOpenFilePicker({
excludeAcceptAllOption: false,
types: [
{
description: 'Text files',
accept: {
'text/plain': ['.txt'],
},
},
],
});
🙌🌰
showOpenFilePicker 是一个异步方法,它会返回一个 Promise 对象,我们可以通过 await 来等待它的结果。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<button onclick="getFile()"> getFile </button>
<script>
async function getFile() {
const filePicked = await window.showOpenFilePicker();
console.log("🚀 ~ file: index.html:17 ~ getFile ~ filePicked:", filePicked)
const [fileHandle] = filePicked
console.log("🚀 ~ file: index.html:18 ~ getFile ~ fileHandle:", fileHandle)
const file = await fileHandle.getFile();
console.log("🚀 ~ file: index.html:19 ~ getFile ~ file:", file)
return file;
}
</script>
</body>
</html>
打印看看 showOpenFilePicker 最后返回的结果:
可以看到返回的结果是一个数组,这是因为我们可以选择多个文件;而这个数组的每一项都是一个FileSystemFileHandle 对象,我们可以通过它来获取和操作文件
window.showSaveFilePicker() 创建文件
showSaveFilePicker 也是文件选择器的一种,它和 showOpenFilePicker 的区别在于,showSaveFilePicker 是用来创建文件的,而 showOpenFilePicker 是用来选择文件的。
showSaveFilePicker 返回的是新创建的文件的 FileSystemFileHandle 对象,而 showOpenFilePicker 返回的是选择的文件的 FileSystemFileHandle 对象数组。
options 参数可选
excludeAcceptAllOption 是否支持所有类型
一个boolean值,默认为false,是否允许用户创建所有类型的文件
suggestedName 推荐名称
types 允许的文件类型
与 showOpenFilePicker 中的相同。
🙌🌰
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<button onclick="saveFile()"> saveFile </button>
<script>
async function saveFile() {
const fileHandle = await window.showSaveFilePicker({
types: [
{
description: 'Text files',
accept: {
'text/plain': ['.txt'],
},
},
],
})
console.log("🚀 ~ file: index.html:26 ~ saveFile ~ fileHandle:", fileHandle)
// 创建一个可写流
const writable = await fileHandle.createWritable();
// 写入数据
await writable.write('Hello World!');
// 关闭流
await writable.close();
}
</script>
</body>
</html>
window.showDirectoryFilePicker() 选择目录
showDirectoryFilePicker 方法会打开目录选择器,用户选择目录后返回 FileSystemDirectoryHandle
对象,我们可以通过它来获取目录的信息。
如果用户在未进行选择的情况下关闭提示,或者所选文件被认为过于敏感或危险而无法暴露给网站,则会引发 AbortError。
options 参数可选
包含选项的对象,如下所示:
id
通过指定 ID,浏览器可以记住不同 ID 的不同目录。如果对另一个选取器使用相同的 ID,则该选取器将在同一目录中打开。
mode
一个字符串,默认为只读访问,或是对目录的读写访问。”read” “readwrite”
startIn
🙌🌰
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<button onclick="getDirectory()"> getDirectory </button>
<script>
async function getDirectory() {
const directoryHandle = await window.showDirectoryPicker()
console.log("🚀 ~ file: index.html:17 ~ getDirectory ~ directoryHandle:", directoryPicked)
}
</script>
</body>
</html>
打印看看 showDirectoryPicker 最后返回的结果:
可以看到,我们通过showDirectoryPicker方法获取到了目录,它返回的是一个FileSystemDirectoryHandle对象
使用 showDirectoryPicker 方法,浏览器会提示用户授权应用程序访问他们的文件系统,不要拒绝