OPFS是浏览器自动分配的沙箱化虚拟文件系统,无需用户交互;入口为window.storage.getDirectory(),操作须纯JS驱动,需调用close()提交写入,不支持HTML标签或真实路径访问。

Origin Private File System(OPFS)不是靠 HTML 标签启用的,<input type="file"> 或 <input webkitdirectory> 都不等于 OPFS —— 它们属于更底层的 File System Access API,和 OPFS 是两套机制。OPFS 是完全沙箱化的、无用户交互的虚拟文件系统,浏览器自动分配空间,不需要用户点选目录。
OPFS 不能用 HTML 标签直接调用
常见误解是把 <input webkitdirectory> 当成 OPFS 入口。它实际触发的是 File System Access API 的 window.showDirectoryPicker(),需要用户手动授权并选择真实路径,和 OPFS 的“自动创建、无需弹窗、不可访问真实磁盘”本质冲突。
-
showDirectoryPicker()返回的是FileSystemDirectoryHandle,指向用户本地目录,有真实路径语义 - OPFS 的入口是
window.storage.getDirectory(),返回FileSystemDirectoryHandle,但它是内存/持久化沙箱中的虚拟目录,没有对应磁盘路径 - 两者同名类名,行为完全不同;混用会导致权限错误或
NotAllowedError - HTML 中任何
input、form、download属性都对 OPFS 无意义,OPFS 操作必须纯 JS 驱动
正确初始化 OPFS 的三步 JS 流程
OPFS 必须在安全上下文(HTTPS 或 localhost)中,通过异步 JS 调用获取句柄。没有 HTML 配置项,也没有 <meta> 开关。
- 先检查支持性:
if ('storage' in window && 'getDirectory' in window.storage),Chrome 122+、Edge 122+、Opera 108+ 支持,Firefox 和 Safari 尚未实现 - 获取根目录:
const root = await window.storage.getDirectory()—— 这一步不弹窗、不请求权限,直接返回沙箱根句柄 - 创建子目录或文件:
const file = await root.getFileHandle('data.bin', { create: true }),注意{ create: true }是必需的,否则getFileHandle()在文件不存在时会拒绝 - 写入必须用
createWritable()+write()+close()三段式,不能省略close(),否则数据可能未落盘
为什么 createWritable() 写入后文件还读不到?
这不是 bug,而是 OPFS 的原子写入设计:createWritable() 创建的是临时缓冲区,close() 才真正提交到文件系统。漏掉 close() 是最常被忽略的坑。
使用 Puppeteer + Chrome 将 HTML 渲染为中文 PDF,自动处理图表等待、Tab 展开、动画、测高、白边消除、防分页,适用于看板、报表、网页和交互图表转 PDF。
立即学习“前端免费学习笔记(深入)”;
- 错误写法:
await writable.write(data);—— 此时文件内容为空或旧内容 - 正确写法:
await writable.write(data); await writable.close(); - 如果中途出错,应
try/catch并确保writable.abort()清理缓冲,否则可能阻塞后续写入 - 读取前务必等
close()完成,否则getFileHandle()取到的仍是旧版本句柄 - OPFS 不支持
fs.appendFile类语义,追加需先getFileHandle()读原内容,再整体重写
OPFS 和 IndexedDB 性能差异在哪?
OPFS 的优势不是“能存更多”,而是“写得快、读得稳”,尤其适合大块二进制流场景。但它不支持索引、查询、事务回滚 —— 这些仍是 IndexedDB 的地盘。
- 100 MB 文件写入:OPFS 同步阻塞式 API 实测约 80–120 ms,IndexedDB 异步批量写入通常 400–900 ms(含序列化开销)
- 随机读取小片段(如视频帧):OPFS 支持
getFileHandle().createSyncAccessHandle()(仅 Chrome),可 mmap 式读取任意 offset,IndexedDB 必须整条 record 加载再切片 - 结构化数据(如用户设置、草稿 JSON):仍该用 IndexedDB,OPFS 没有键值索引,查一条要遍历所有文件
- OPFS 文件名长度限制为 255 字符,且不支持
/、\0等非法字符,命名前必须 sanitize
OPFS 的核心约束很朴素:它不是通向硬盘的后门,而是一块浏览器托管的、高性能的、隔离的“私有硬盘分区”。所有操作都在 JS 主线程同步完成,没有回调地狱,但也意味着大文件写入会阻塞 UI —— 这恰恰是它比 IndexedDB 更接近原生文件 I/O 的体现。别试图绕过 close(),也别指望用它替代数据库逻辑。


















