跳转到内容

File System Access API:读写本地文件

一句话: File System Access API 让 Web 应用通过 showOpenFilePickershowSaveFilePickershowDirectoryPicker 打开、编辑并保存用户设备上的真实文件与 文件夹。它需要安全上下文与用户手势,每次写入都受显式权限提示约束,且仅在 Chromium 浏览器中提供——所以 Safari 与 Firefox 需要回退方案。

  • showOpenFilePicker() 返回一组 FileSystemFileHandle。调用 handle.getFile() 读取 File(一个 Blob)——用 .text().arrayBuffer().stream() 获取内容。
  • showSaveFilePicker() 返回一个指向新建或选定文件的 FileSystemFileHandle。调用 handle.createWritable() 获取 FileSystemWritableFileStream,向其 write(),再 close() 提交。写入先进临时文件,关闭时原子替换目标文件。
  • showDirectoryPicker() 返回一个 FileSystemDirectoryHandle。用 for await (const [name, handle] of dir.entries()) 遍历条目,并通过 getFileHandle(name, { create }) / getDirectoryHandle(name, { create }) 创建或获取子项。

三个选择器都是异步的,用户取消时以 AbortError 拒绝,且必须在安全上下文(HTTPS 或 localhost)中由用户手势(点击、按键)触发。

  • 用户选择文件或文件夹时即授予读取权限。写入会再次提示——首次 createWritable() (或显式 requestPermission({ mode: 'readwrite' }))会触发权限对话框。
  • handle.queryPermission({ mode }) 在不提示的情况下查询当前状态;用 handle.requestPermission({ mode })(必须在用户手势内)请求权限。两者都解析为 'granted''denied''prompt'
  • 授权按源(origin)限定且并非永久——通常只在标签页/会话期间有效,页面完全关闭后 重置,因此再次访问时重新提示是预期行为。
  • 某些敏感位置(系统文件夹、某些情况下的下载目录)会被直接禁止访问。

FileSystemFileHandleFileSystemDirectoryHandle可结构化克隆的,因此你可以 把它们存入 IndexedDB,并在之后的访问中取回——用户无需重新选择同一文件。句柄会持久化, 但权限不会:恢复句柄后,需调用 queryPermission(),并在必要时由新的用户手势触发 requestPermission() 再进行读写。这一模式正是 Web 端编辑器与 IDE 中“最近文件”列表的 实现基础。

OPFS 是本 API 中广泛可用的子集。navigator.storage.getDirectory() 返回一个根植于私有、 按源限定沙箱的 FileSystemDirectoryHandle——无选择器、无权限提示,且不在用户文件管理器 中可见。它在包括 Safari 与 Firefox 在内的所有现代引擎中均可用,支持通过 createSyncAccessHandle() 在 Web Worker 中进行高性能同步访问,非常适合浏览器内 SQLite、 缓存与大型工作数据。需要快速本地存储时用 OPFS;需要用户看见并拥有真实文件时用选择器。

Browser / PlatformSupportSinceConfidenceSourceNotes
Chrome (Android)❌ nohighrefshowOpenFilePicker/showSaveFilePicker are not exposed on Android.
Chrome (Desktop)✅ yes86highref
Edge (Desktop)✅ yes86highref
Safari (iOS)❌ nomediumrefOnly the origin-private file system (OPFS) is available, not the user-visible picker.
Safari (macOS)❌ nomediumrefNo showOpenFilePicker; OPFS only.
Firefox (Desktop)❌ nomediumrefOPFS only; no user-visible file picker access.
Samsung Internet❌ nomediumref

Source: spec · MDN · Last verified 2026-06-24 · Confidence: high

决策问题 建议行为 理由
需要用户打开并重新保存自己的文件(编辑器、IDE)? 用选择器 + 把句柄存入 IndexedDB。 真正的就地编辑;“最近文件”可跨会话工作。
同时面向 Safari 或 Firefox? <input type="file"> 打开、用 <a download> blob URL 保存作为回退。 选择器仅限 Chromium;此方案覆盖所有引擎且体验平滑。
只需快速私有存储(缓存、数据库、暂存空间)? navigator.storage.getDirectory() 走 OPFS。 随处可用、无提示、Worker 同步访问保证性能。
写入大文件或流式输出? createWritable() 分块 write(),再 close() 关闭时原子替换;避免把整个文件缓冲进内存。
在之后的访问中恢复已保存句柄? 在点击处理器内 queryPermission(),再 requestPermission() 句柄持久但授权不持久;必须重新提示。
  • 仅在用户手势内、且仅在 HTTPS/localhost 下调用选择器。
  • 捕获 AbortError——用户取消选择器是正常现象,不是错误。
  • 始终 close() 可写流,以便原子写入提交。
  • 把句柄存入 IndexedDB,但返回时用 query/requestPermission 重新核验权限。
  • 为 Safari 与 Firefox 提供 <input type="file"> + 下载链接回退。
  • 需要私有、跨引擎、高性能存储而非用户可见文件时,选用 OPFS。