想象一下,如果你想使用一个图片编辑器,或是码代码的时候,不用走弯路,直接在网页上打开、保存、编辑甚至删除你电脑里的图片或者源代码那该多牛X。没错,File System Access API 就是web端实现这一切的基础。最近我就用这个API写了一个在线处理stable diffusion的图片文件的应用,所以趁还没忘掉把知识点记一下,想体验的话也可以点我试试
开始之前:安全第一!
要特别强调的一点是:安全是这个 API 的设计之本。你不可能在用户毫无察觉的情况下闯入他们的文件系统。用户必须明确授权后,你的web应用才能正常的操作文件。
获取文件句柄:开启沟通之门
要想读取或写入文件,我们首先得有个"入场券"——也就是文件句柄(FileSystemFileHandle)。这个句柄基本上就是一个代表文件的对象,通过它,我们可以对文件做一系列的操作。
怎样获取呢? 俗话说得好,打狗还要看主人呢,咳咳,可能不咋恰当,但意思就是这个意思——要想操作文件,就得先经过用户点头。下面这段代码展示了如何以得体又礼貌的方式请求用户选择文件:
async function getFileHandle() {
try {
const [fileHandle] = await window.showOpenFilePicker();
// 处理 fileHandle...
} catch (error) {
// 用户不想选择文件?没关系,我们理解。
console.warn('文件选择被取消', error);
}
}
从文件读取内容
有了句柄,读文件就跟读小说一样简单:
// handle就是上面拿到的文件句柄
async function readFromFile(handle) {
const file = await handle.getFile();
const content = await file.text();
// content 就是文件内容啦!展示给用户,或做些其他花活吧!
}
向文件写入内容
能读,肯定就能写
//handle 为文件句柄 content就是你想修改的内容了,可以是字符串,也可以是二进制数据
async function writeToFile(handle, content) {
const writable = await handle.createWritable();
// 现在开始写!
await writable.write(content);
// 写完记得关门——关闭文件流
await writable.close();
}
创建或删除文件和目录
// parentHandle为父文件夹的句柄 比如你在A文件夹下创建B文件夹 那么A文件夹的句柄就是parentHandle 而B文件夹就是newFolderName
async function createNewDirectory(parentHandle) {
const newFolderName = '我是新文件夹';
const newDirectoryHandle = await parentHandle.getDirectoryHandle(newFolderName, { create: true });
// 你的新目录已经准备好啦!
}
// 这里的handle可以是文件句柄 也可以是 文件夹句柄
async function removeItem(handle) {
await handle.remove();
// 它已经不在了,就像从来没有出现过一样。
}
管理文件权限
权限很重要,万一用户有意或无意的收回了我们的权限怎么办?虽然不太可能出现,但我们可以检查并请求权限:
// fileHandle和 readFromFile 中的 handle 类似,这里的 fileHandle 是一个 FileSystemFileHandle 对象,代表一个具体的文件。
// readWrite是一个布尔值,如果 readWrite 是 true,那么我们将请求用于读和写操作的权限;如果是 false 或未定义,则只请求读权限
async function verifyPermission(fileHandle, readWrite) {
const options = {};
if (readWrite) {
options.mode = 'readwrite';
}
// 试试看我们还有没有权限
if ((await fileHandle.queryPermission(options)) === 'granted') {
return true;
}
// 如果没有,咱们再问一次好不好?
if ((await fileHandle.requestPermission(options)) === 'granted') {
return true;
}
// 看来是真的没戏了。
return false;
}