在传统浏览器中,浏览器应用程序只能访问网页资源,而网页资源是存储在服务器上的。这意味着像 Photoshop、VSCode 等应用程序无法在浏览器中运行,因为它们需要访问本地文件。浏览器文件系统 API 的出现,打破了这一局限,为像 Photoshop、VSCode 等应用程序在浏览器中运行提供了基础。下面我们来看看浏览器怎么操作系统文件。
showOpenFilePicker 选取文件
showOpenFilePicker()
方法用于显示一个文件选择器,允许用户选择一个或多个文件。返回值是一个 File[] 数组,其中包含用户选择的文件句柄。当未指定任何选项时,只允许用户选择单个文件。
html
<button @click="openFilePicker">打开文件</button>
js
export default {
methods: {
async openFilePicker() {
const [fileHandle] = await window.showOpenFilePicker()
// 文件操作
},
},
}
点击按钮后,效果如下图所示:
读取文件
有了文件句柄,我们可以获取文件的属性,或者访问文件本身。通过getFile()
获取 File 对象,然后通过 File 对象的方法(如slice()
、stream()
、text()
、arrayBuffer()
)来读取文件内容。
js
const file = await fileHandle.getFile()
const contents = await file.text()
保存文件
showSaveFilePicker()
方法用于保存文件,可以创建一个新的文件,如创建一个空的 .txt 文件。
js
async getNewFileHandle() {
const options = {
types: [
{
description: 'Text Files',
accept: {
'text/plain': ['.txt'],
},
},
],
};
const handle = await window.showSaveFilePicker(options);
return handle;
}
以下是一些 options
的设置:
- suggestedName :设置默认文件名,例如
suggestedName: 'Untitled Text.txt'
。 - startIn :设置默认启动目录,例如
startIn: 'desktop'
会打开桌面目录,还有其他可选的值包括documents
、downloads
、music
、pictures
、videos
等。当然也可以是已知的文件或目录句柄。 - id :通过设置
id
,可以让文件系统记住你打开过的目录位置。例如,当新建文件时设置id: "img"
,第一次选取了目录/path/to/img
,下次使用id: "img"
时就能够直接定位到这个目录。
写入文件
要将数据写入磁盘,可以使用 FileSystemWritableFileStream
对象,它是 WritableStream
的一个子类。通过调用文件句柄对象创建流。在调用时,浏览器会首先检查用户是否已经授予对文件的写入权限。如果用户尚未授予写入权限,浏览器会提示用户进行授权。如果用户没有授权,则无法写入文件。
js
async writeFile(fileHandle, contents) {
// 创建一个 FileSystemWritableFileStream 用于写入
const writable = await fileHandle.createWritable();
// 写入我们的文件
await writable.write(contents);
// 关闭文件并将内容写入到磁盘
await writable.close();
}
利用文件写入操作,还可以应用于流式下载文件。传统的文件下载方式使用 <a>
标签,需要加载完整文件,等待时间长,而且会消耗大量内存。使用流式下载就可以解决这个问题,而且可以摆脱依赖于像 StreamSaver.js
这样的库。
js
async function writeURLToFile(fileHandle, url) {
const writable = await fileHandle.createWritable()
const response = await fetch(url)
await response.body.pipeTo(writable)
}
showDirectoryPicker 选取目录
使用 showDirectoryPicker()
方法可以让用户选择一个目录,然后返回一个 FileSystemDirectoryHandle
对象。该目录句柄允许您枚举和访问该目录中的文件。默认情况下,您具有对目录中文件的读取权限,但如果需要写入权限,可以通过 { mode: 'readwrite' }
参数进行传递。
js
async openDirectoryPicker() {
const dirHandle = await window.showDirectoryPicker();
for await (const entry of dirHandle.values()) {
console.log(entry.kind, entry.name);
}
}
创建文件夹或文件
对于已经存在的目录句柄,可以使用 getDirectoryHandle()
方法创建文件夹,或使用 getFileHandle()
方法创建文件。
js
// 新建文件夹
const newDirectoryHandle = await existingDirectoryHandle.getDirectoryHandle('New Documents', {
create: true,
})
// 新建文件
const newFileHandle = await newDirectoryHandle.getFileHandle('New Notes.txt', { create: true })
值得注意的是,使用 getFileHandle()
创建文件时不会弹出文件选择框,而是直接将文件添加到您的系统文件中,这一点与前面提到的保存新文件是不一样的。
删除文件夹或文件
可以使用 removeEntry()
方法来删除文件和文件夹。对于文件夹,可以选择递归删除,包括所有子文件夹和其中包含的文件。
js
// 删除文件
await newDirectoryHandle.removeEntry('New Notes.txt')
// 删除文件夹
await existingDirectoryHandle.removeEntry('New Documents', { recursive: true })
还可以直接使用文件或文件夹句柄的 remove()
方法进行删除。
js
await newDirectoryHandle.remove({ recursive: true })
await newFileHandle.remove()
删除目录时,如果目录不为空,需要添加 { recursive: true }
参数来递归删除子目录和文件,否则无法删除目录。
移动或重命名
可以使用 move()
方法来移动或重命名文件。实际上,重命名操作本质上也是一个移动操作。
js
// 文件重命名为 Notes.txt
await newFileHandle.move('Notes.txt')
// 移动到新的文件夹
await newFileHandle.move(existingDirectoryHandle)
// 移动到新的文件夹并重命名为Notes.txt
await newFileHandle.move(existingDirectoryHandle, 'Notes.txt')
浏览器文件系统API的强大之处令人惊叹,它为未来浏览器应用程序的发展创造了广阔的前景。随着技术的不断进步,我们可以预见到更多应用程序将转移到浏览器平台,并利用文件操作API的优势。