前言
前一段时间,在做一套自定义换肤方案,突发奇想,换肤配置去后端化,是不是可以通过前端读取主题中的色卡相关的数据,可以在网页上来配置主题色和基础色,然后生成或者修改换肤文件。这时候就往浏览器怎么样可以对本地项目文件的访问、读取文件、写入或创建等方面思考。
demo在线体验地址: v-theme-colors
源码地址: v-theme-colors(ps 大部分功能暂未同步发布)
查阅了一下资料,惊奇的发现,File System Access API(下文简称: 文件系统访问API ) 是一种比较好的解决方案。它是 Web API,它允许对用户的本地文件进行读写访问。本文带大家一起探索一下 文件系统访问API 的正确打开方式吧~
一、使用 文件系统访问API 读取文件 :window.showOpenFilePicker()
在深入研究从用户系统读取文件所需的代码之前,需要记住的一个重要细节是调用文件系统访问 API 需要通过用户手动在安全上下文中完成。在下面的示例中,我们是之间在浏览器控制台上操作的
从单个文件中读取
从文件中读取数据只需不到 10 行代码即可完成。这是一个示例代码示例:
let fileHandle
const pickFile = async () => {
[fileHandle] = await window.showOpenFilePicker()
console.log('fileHandle',fileHandle)
const file = await fileHandle.getFile()
const content = await file.text()
console.log('content',content)
return content
};
我们可在浏览器控制台输入以上代码,测试一下,执行pickFile()
函数,可以发现浏览器唤起了本地选择文件
我们通过调用启动文件选择器window.showOpenFilePicker()
,并将此查询的结果存储在名为 的变量中fileHandle
。
我们从调用中得到的是代表我们选择的每个文件showOpenFilePicker()
的对象数组。FileSystemFileHandle
由于此示例针对单个文件,因此我们对结果进行了解构。
我们操作一下选择,我本机的文件夹中的base-colors.js
可以看到在控制台上这个方法返回一个promise,并分别打印fileHandle
和content
可以获取到这个文件里面的内容:
fileHandle
对象包含一个kind
andname
属性
FileSystemFileHandle {kind: 'file', name: 'base-colors.js'}
kind
可以是 file
(文件) 或 directory
(文件夹)
通过 fileHandle
,我们可以调用该getFile()
方法来获取有关我们文件的详细信息。调用此方法会返回一个具有一些属性的对象,包括文件上次修改时间的时间戳、文件名、文件大小和类型。
最后,我们可以调用text()
文件来获取其内容。
从多个文件中读取
要从多个文件中读取,我们需要将一个options
对象传递给showOpenFilePicker()
.
let fileHandles;
const options = { multiple: true, };
const pickFile = async () => {
fileHandles = await window.showOpenFilePicker(options);
};
默认情况下,该multiple
属性设置为false
。其他选项可用于指示可以选择的文件类型。例如,如果我们只想接受.jpeg
文件,选项对象将包括以下内容
const options = {
types: [
{
description: "Images",
accept: { "image/jpeg": ".jpeg", },
},
],
excludeAcceptAllOption: true,
};
在这个例子中,fileHandles
是一个包含多个文件的数组,因此获取它们的内容将通过以下方式完成:
let fileHandles;
const options = { multiple: true, };
const pickFile = 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 还允许您将内容写入文件。首先,让我们看看如何保存新文件。
写入新文件
写入新文件也可以用很短的代码完成!
const saveFile = 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;
};
在执行saveFile
函数时,我们使用打开文件选择器,showSaveFilePicker()
并传入一个option
包含要保存的文件类型的对象,这里是一个.txt
文件。
调用此方法还将返回一个FileSystemFileHandle
对象,如第一部分中所示。在这个对象上,我们可以调用createWritable()
将返回一个FileSystemWritableFileStream
对象的方法。write()
然后,我们可以使用我们需要传递内容的方法将一些内容写入此流。
最后,我们需要调用该close()
方法来关闭文件并完成将内容写入磁盘。
例如,如果您想将一些 HTML 代码写入文件,您只需更改options
对象中的内容以接受"text/html": [".html"]
并将一些 HTML 内容传递给该write()
方法。
三、编辑现有文件
如果您想导入文件并使用文件系统访问 API 对其进行编辑,示例代码示例如下所示:
let fileHandle;
const writeFile = async () => {
[fileHandle] = await window.showOpenFilePicker();
const file = await fileHandle.getFile();
const writable = await fileHandle.createWritable();
await writable.write("这是新写入的内容");
await writable.close();
};
如果您一直在关注本文的其余部分,您可能会认识到我们从showOpenFilePicker()
和getFile()
方法开始读取文件,然后使用createWritable()
,write()
并close()
写入同一个文件。
如果您要导入的文件已有内容,则此代码示例将用传递给write()
方法的新内容替换当前内容
弹框确认后,打开文件夹发现,已经更改了文件里面的内容
四、其他文件系统访问 API 功能
无需过多介绍,文件系统访问 API 还允许您列出目录中的文件并删除文件或目录。
4.1 读取目录
读取目录可以用一点点代码完成:
const readDir = async () => {
const directoryHandle = await window.showDirectoryPicker();
for await (const entry of directoryHandle.values()) {
console.log(entry.kind, entry.name);
}
}
执行readDir()
方法,调用该showDirectoryPicker()
方法将打开文件选择器,并且在计算机上选择目录时,该代码将列出该目录中的文件,我们测试一下,选中我本地的theme文件夹
发现控制台可以准确的打印出文件目录
4.2 删除文件
可以使用以下代码示例删除目录中的文件:
const removeFile = async () => {
const [fileHandle] = await window.showOpenFilePicker();
await fileHandle.remove();
};
如果你想删除一个文件夹,你只需要对上面的代码示例做一个小的改动:
const removeDirectory = async () => {
const directoryHandle = await window.showDirectoryPicker();
await directoryHandle.remove();
};
最后,如果要在选择文件夹时删除特定文件,可以这样写:
// 删除选定文件夹中名为data.txt的单个文件
const removeSelfFile = async () => {
const directoryHandle = await window.showDirectoryPicker();
await directoryHandle.removeEntry("data.txt");
};
如果要删除整个文件夹,则需要以下行:
// 递归删除名为“data”的文件夹
const removeAllDirectory = async () => {
const directoryHandle = await window.showDirectoryPicker();
await directoryHandle.removeEntry('data', { recursive: true });
};
五、文件系统访问API 浏览器支持
看到上面 文件系统访问API 的使用,各位肯定会问它的浏览器支持情况是怎么样的,正如下图基本上涵盖大多数浏览器的使用,此浏览器支持数据来自Caniuse,其中包含更多详细信息。数字表示浏览器支持该版本及更高版本的功能
最后
文件系统访问API,它解锁了构建强大 Web 应用程序的新功能,例如文本编辑器或IDE、图像编辑工具、改进的导入/导出等等,所有这些都可以在前端进行操作,你也可以做更多有趣的应用。
如果您想试用文件系统访问API,可以查看 Google构建的现场演示文本编辑器。,如果想更深入了解 File System Access API 及其所有功能的更多信息,可以参阅以下资源:
- 文件系统访问 API(W3C 规范)
- 文件系统访问 API (MDN)
- 对比度范围、replaceAll 方法、本机文件系统 API (Šime Vidas)
- 文件系统访问 API:简化对本地文件的访问(web.dev)
- 使用 browser-fs-access 库(web.dev)读取和写入文件和目录
- browser-fs-access 存储库(GitHub)