浏览器读写本地文件 File System Access API
原创大约 11 分钟...
安全上下文
此 API 仅在 安全上下文 (HTTPS)
中可用,在某些或所有支持浏览器中可用。
▶️ 文件系统访问 API (File System Access API)
允许与用户本地设备或用户可访问的网络文件系统上的文件进行交互。此 API 的核心功能包括读取文件、写入或保存文件以及对目录结构的访问。
浏览器支持
浏览器版本支持情况:
86 x 86 x// 粗暴的检测下当前环境是否支持
const supported = window?.showOpenFilePicker && window?.showDirectoryPicker;
console.log(supported ? "支持" : "不支持");
您当前的浏览器: 不支持
部分代码在这里
// 浏览器下载
const browserDownload = async function () {
const blob = await genDemoExcel();
saveAs(blob, "浏览器下载.xlsx");
};
// api写入
const apiWrite = async function (content, filename) {
const blob = await genDemoExcel();
saveAsByApi(blob, "api写入.xlsx");
};
// 生成示例 excel
const genDemoExcel = async function () {
const wb = new ExcelJS.Workbook();
let now = new Date();
wb.creator = "www.qinwg.cn";
wb.lastModifiedBy = "www.qinwg.cn";
wb.created = now;
wb.modified = now;
wb.lastPrinted = now;
// 工作表1
const sheet = wb.addWorksheet("变量表");
let row1 = sheet.addRow(["姓名", "年龄", "身高(cm)"]);
row1.font = {
bold: true,
};
sheet.addRow(["李大狗", 18, 180]);
sheet.addRow(["李二狗", 20, 175]);
sheet.addRow(["李三狗", 19, 179]);
// 写入 buffer
const buffer = await wb.xlsx.writeBuffer();
// 下载
let blob = new Blob([buffer], {
type: "application/vnd.ms-excel;charset=utf-8",
});
return blob;
};
// js实现浏览器下载
const saveAs = function (blob, filename) {
if (window.navigator.msSaveOrOpenBlob) {
//IE保存Blob对象
navigator.msSaveBlob(blob, filename);
} else {
//其他浏览器
var link = document.createElement("a");
var body = document.querySelector("body");
/*window.URL.createObjectURL可以用于在浏览器上预览本地图片或者视频,创建一个url*/
link.href = window.URL.createObjectURL(blob); //为Blob对象创建一个url地址
link.download = filename;
// 如果不设置download属性的话,那么如果是jpg、txt等浏览器支持打开的属性,就会在浏览器打开该文件而不是下载下来
// fix Firefox
link.style.display = "none";
body.appendChild(link);
link.click();
body.removeChild(link);
window.URL.revokeObjectURL(link.href); // 释放地址
}
};
// api写入
const saveAsByApi = function (content, filename) {
window
.showSaveFilePicker({
suggestedName: filename,
})
.then(async (writeHandle) => {
const writableStream = await writeHandle.createWritable();
await writableStream.write(content);
await writableStream.close();
console.log(`保存成功: ${writeHandle.name}`);
})
.catch((err) => {
console.error("写入失败: ", err);
});
};
onMounted(() => {
// 引入 exceljs 库
const jsLib = document.createElement("script");
jsLib.src = "https://cdn.bootcdn.net/ajax/libs/exceljs/4.3.0/exceljs.min.js";
document.body.appendChild(jsLib);
});
方法封装: saveBlobAs() (自动判断用 [浏览器下载] 还是 [API 直接写入文件])
/**
* 浏览器保存文件
* 不支持 [File System Access API] 时自动转为浏览器下载
*/
const saveBlobAs = function (blob, filename) {
// 支持本地文件直接访问(仅在 chrome 86 以上, https 协议或者 localhost 下可用)
if (window.showSaveFilePicker) {
window
.showSaveFilePicker({
suggestedName: filename,
})
.then(async (writeHandle) => {
// 创建一个 FileSystemWritableFileStream 用于写入
const writableStream = await writeHandle.createWritable();
// 写入文件
await writableStream.write(blob);
// 关闭文件并写入内容到磁盘
await writableStream.close();
console.log(`保存成功: ${writeHandle.name}`);
})
.catch((err) => {
console.log("保存失败: ", err);
});
} else if (window.navigator.msSaveOrOpenBlob) {
//IE保存Blob对象
navigator.msSaveBlob(blob, filename);
} else {
//其他浏览器
var link = document.createElement("a");
var body = document.querySelector("body");
/*window.URL.createObjectURL可以用于在浏览器上预览本地图片或者视频,创建一个url*/
link.href = window.URL.createObjectURL(blob); //为Blob对象创建一个url地址
link.download = filename;
// 如果不设置download属性的话,那么如果是jpg、txt等浏览器支持打开的属性,就会在浏览器打开该文件而不是下载下来
// fix Firefox
link.style.display = "none";
body.appendChild(link);
link.click();
body.removeChild(link);
window.URL.revokeObjectURL(link.href); // 释放地址
}
};
基本使用
基础方法:
打开文件
语法
showOpenFilePicker();
参数
options
可选参数multiple
- 布尔值;是否多选. 默认
false
.
- 布尔值;是否多选. 默认
excludeAcceptAllOption
- 布尔值;是否排除
接受所有
选项. 默认false
.
- 布尔值;是否排除
types
- 数组;指定文件类型.
示例
// 选择文件
const openFile = (multiple = false) => {
window
.showOpenFilePicker({
multiple: multiple, // 多选
excludeAcceptAllOption: true,
types: [
{
description: "选择图片",
accept: {
"image/*": [".png", ".gif", ".jpeg", ".jpg"],
},
},
],
})
.then((fileHandles) => {
console.log(fileHandles);
let files = fileHandles
.map((fileHandle) => fileHandle.name || "")
.join("\n");
console.log(`选择了 ${fileHandles.length} 个文件: \n${files}`);
})
.catch((err) => {
console.log("选择文件失败:", err);
});
};
// 查看文本文件内容
const openTextFile = () => {
window
.showOpenFilePicker({
multiple: false, // 取消多选
excludeAcceptAllOption: true,
types: [
{
description: "选择文本文件",
accept: {
"text/plain": [".txt"],
},
},
],
})
.then((fileHandles) => {
const [fh] = fileHandles;
// console.log(fh);
fh.getFile().then((file) => {
console.log("file:", file);
// 读取文本内容
const reader = new FileReader();
reader.onload = (e) => {
console.log(
`打开文件: ${fh.name}\n文件内容:\n\n%c${e.target.result}`,
"color:#35ccbf;fontsize: 20px;"
);
};
reader.readAsText(file);
});
})
.catch((err) => {
console.error("选择文件失败: ", err);
});
};
演示
showOpenFilePicker 点击右侧按钮查看代码
写入文件
语法
showSaveFilePicker();
参数
options
可选参数excludeAcceptAllOption
- 布尔值;是否排除
接受所有
选项. 默认false
.
- 布尔值;是否排除
suggestedName
- 字符串. 建议的文件名.
types
- 数组;指定文件类型.
示例
// 写入文本内容到文本文件
window
.showSaveFilePicker({
suggestedName: "写入测试.txt", // 待写入的文件名
})
.then(async (writeHandle) => {
// create a FileSystemWritableFileStream to write to
const writableStream = await writeHandle.createWritable();
/* writableStream.write 的可选参数
// just pass in the data (no options)
writableStream.write(data);
// writes the data to the stream from the determined position
writableStream.write({ type: "write", position, data });
// updates the current file cursor offset to the position specified
writableStream.write({ type: "seek", position });
// resizes the file to be size bytes long
writableStream.write({ type: "truncate", size });
*/
// 写入文件
await writableStream.write("这里是要写入的文本内容\n换一行吧\n\n再写一行");
// close the file and write the contents to disk.
await writableStream.close();
console.log(`保存成功: ${writeHandle.name}`);
})
.catch((err) => {
console.error("写入失败: ", err);
});
演示
showSaveFilePicker 点击右侧按钮查看代码
打开目录
语法
const FileSystemDirectoryHandle = await window.showDirectoryPicker();
参数
options
可选参数id
- 通过指定 ID,浏览器可以记住不同 ID 的不同目录。如果将同一 ID 用于另一个选取器,则该选取器将在同一目录中打开。
mode
- "read","readwrite",默认为只读访问。
startIn
- 打开目录时的起始位置,可以是一个 FileSystemHandle 或者一个众所周知的目录如: ("desktop", "documents", "downloads", "music", "pictures", or "videos")。
示例
// 选择目录
window
.showDirectoryPicker({
// id: "firstDir",
mode: "read", // "read" 或 "readwrite", 默认只读
/**
* 起始位置, 常见的如 "desktop", "documents", "downloads",
* "music", "pictures", or "videos" 等用户目录,
* 或者一个 FileSystemHandle 对象
*/
startIn: "downloads",
})
.then(async (dirHandle) => {
console.log(dirHandle);
let files = [];
for await (const [key, value] of dirHandle.entries()) {
files.push(key);
}
console.log(
`打开目录: ${dirHandle.name}\n` + `文件列表: \n${files.join("\n")}`
);
})
.catch((err) => {
console.error("打开目录失败: ", err);
});
演示
showDirectoryPicker