navigator.storage.getDirectory 返回 undefined 或抛出 NotSupportedError,是因为其依赖安全上下文(HTTPS/localhost)和用户主动交互;Chrome 94+支持,Firefox/Safari不支持。

为什么 navigator.storage.getDirectory 返回 undefined 或抛出 NotSupportedError
直接调用 navigator.storage.getDirectory 大概率失败,不是代码写错了,而是它依赖两个硬性前提:页面必须运行在 安全上下文(HTTPS 或 localhost),且用户已主动触发过一次交互(如点击、按键)。很多开发者在开发服务器(比如 http://127.0.0.1:3000)或自动执行的脚本里试,结果返回 undefined 或抛出 NotSupportedError: Storage API not supported。
检查方法很简单:
- 打开控制台,执行
self.isSecureContext—— 必须为true - 确认当前文档的
document.visibilityState === 'visible'且有 recent user activation(可用document.hasStorageAccess()粗略判断) - Chrome 94+、Edge 94+ 支持,Firefox 和 Safari 完全不支持 该 API
如何正确获取私有文件系统根目录并持久化访问权限
拿到 FileSystemDirectoryHandle 不是一次性的事,尤其对“海量素材”这种需长期读写的场景,必须显式请求持久化权限,否则刷新后手柄失效,再次调用 getDirectory 会触发权限弹窗(用户可能拒绝)。
实操步骤:
- 先用
navigator.storage.getDirectory()获取初始句柄 - 立即调用
handle.requestPermission({mode: 'readwrite'})并 await - 再调用
await navigator.storage.persist(),并检查await navigator.storage.persisted()是否返回true - 把句柄存在 IndexedDB(不能存句柄本身,要存
handle.toWellKnownString())或 localStorage(仅调试用),后续用self.showOpenFilePicker或self.showDirectoryPicker恢复(注意:getDirectory()无法从字符串还原句柄)
⚠️ 关键点:浏览器不会自动记住你选过的目录。所谓“持久化”,只是延长了当前 origin 下的访问窗口,并不等于永久免授权。
处理海量素材时,getDirectory 后的遍历与读取瓶颈在哪
navigator.storage.getDirectory() 返回的是一个 handle,它本身不加载任何文件内容——真正耗时的是后续的 handle.values()(获取子项迭代器)和逐个 fileHandle.getFile()。对含数万文件的目录,直接展开全量遍历会卡死 UI,且容易触发浏览器的“长时间运行脚本”中断。
建议策略:
- 永远用
for await (const entry of handle.values())替代Array.from(await handle.keys()),避免一次性构造大数组 - 对素材做分页/懒加载:只预读首层子目录名,用户点开某目录后再递归读其内容
- 读取单个文件时,用
fileHandle.getFile().then(f => f.arrayBuffer()),不要用text()或stream()直接解析大图/视频(易 OOM) - 考虑配合
createWritable()+transferTo()做后台写入,避免主线程阻塞
为什么不能直接用 getDirectory 替代 IndexedDB 存储结构化元数据
私有文件系统(origin private file system)是为二进制大文件设计的,不是数据库。它没有索引、查询、事务、全文搜索能力。如果你有 10 万张图,每张带标签、尺寸、拍摄时间等字段,把 JSON 元数据按文件存(如 img_001.json)会导致:
- 每次查“所有风景类 PNG”需遍历全部 JSON 文件并解析,I/O 和 CPU 成本爆炸
- 并发读写同一目录下的多个文件,浏览器可能限制句柄数量(Chrome 默认约 100 个活跃
FileSystemFileHandle) - 无法原子性更新一组关联数据(比如改一张图的标签同时删掉缩略图)
正确做法:用 IndexedDB 存所有元数据 + 路径映射,用私有文件系统只存原始素材二进制流。两者通过唯一 ID 关联,各司其职。
最常被忽略的一点:私有文件系统里的路径名不保证唯一性(不同目录下可有同名文件),而 IndexedDB 的 key 是强制唯一的——这个差异在做路径同步时极易引发静默覆盖或查询丢失。

















