在浏览器中进行文件操作
在开发 WebApp 时可能会遇到文件相关的操作,比如上传文件到服务器、下载文件到本地、缓存文件等,下面会介绍几种不同的方式进行文件操作。
基于标签的上传和下载
最常用的文件上传方式应该是使用 input 标签,通过设置 input 标签的 type=”file”
可以允许用户从本地选择文件进行上传。
function InputFile() {
const [file, setFile] = useState<File | null>(null);
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const file = e.target.files?.[0];
if (!file) return;
setFile(file);
};
return <input onChange={handleChange} type="file" />
}
文件访问 API
文件系统访问 API(File System Access API) (opens in a new tab)属于文件系统 API (opens in a new tab) 的一部分,可以通过使用 API 在用户的操作下完成文件的读写。
在使用该 API 进行文件操作时会使用以下接口
- showOpenFilePicker (opens in a new tab):用于显示一个文件选择器并允许用户选择一个或多个文件,然后返回这些文件的句柄;
export function PickerFS() {
const [file, setFile] = useState<File | null>(null);
const handleChooseFile = async () => {
const fileHandles = await window.showOpenFilePicker();
const file = await fileHandles[0].getFile();
setFile(file);
};
return <Button onClick={handleChooseFile}>Click</Button>
}
- showSaveFilePicker (opens in a new tab):用于显示一个文件选择器并允许用户保存一个文件(覆盖或者新建);
export function PickerFS() {
const handleChooseFile = async () => {
const directoryHandle = await window.showDirectoryPicker();
const keys = directoryHandle.keys();
// 打印该目录下所有文件的名字
for await (const key of keys) {
console.log(key);
}
};
return <Button onClick={handleChooseFile}>Click</Button>
}
- showDirectoryPicker (opens in a new tab):用于显示一个目录选择器并允许用户选择一个目录;
export function PickerFS() {
const [file, setFile] = useState<File | null>(null);
const handleDownloadFile= async () => {
const opts = {
suggestedName: "test.txt",
types: [
{
description: "Text file",
accept: { "text/plain": [".txt"] },
},
],
};
const fileHandle = await window.showSaveFilePicker(opts);
const writable = await fileHandle.createWritable();
await writable.write("Hello, world!");
await writable.close();
};
return <Button onClick={handleDownloadFile}>Click</Button>
}
源私有文件系统
源私有文件系统 (opens in a new tab)跟上面的文件访问系统类似,都是文件系统 API 的一部分,但是它们有个最直接的差异就是是否对用户可见。showXXX 接口都需要打开文件(目录)选择器,并且需要用户主动选择文件(目录),保存的文件也是需要保存到用户指定的路径,但是源私有文件系统的交互不会对用户可见,并且保存的文件是经过处理的数据,用户无法看到原始数据。
export function OpFs() {
const handleChooseFile = async (event: React.ChangeEvent<HTMLInputElement>) => {
const fileList = event.target.files;
const file = fileList && fileList[0];
if (!file) return;
const opfsRoot = await navigator.storage.getDi rectory();
const fileHandle = await opfsRoot.getFileHandle(file.name, { create: true });
const writable = await fileHandle.createWritable();
await writable.write(file);
await writable.close();
};
return <InputFile onChange={handleChooseFile} />;
}
await navigator.storage.getDirectory()
返回一个表示用户本地文件系统根目录的文件句柄,然后通过 getFileHandle
获取指定文件的句柄,create 为 true 表示如果没有该文件的话就会创建一个,接着使用 createWritable
创建可写流,开发者可以通过这个可写流向指定文件写入数据,最后关闭可写流。