showdirectorypicker 仅 chromium 系统支持(chrome 86+、edge 91+、opera 72+),firefox 和 safari 不支持;需 https 或 localhost 安全上下文,file:// 协议下会失败。

showDirectoryPicker 为什么不能直接用在所有浏览器上
这个 API 目前只在 Chromium 系列(Chrome 86+、Edge 91+、Opera 72+)中可用,Firefox 和 Safari 完全不支持。调用 showDirectoryPicker() 前必须确保页面运行在安全上下文(https:// 或 localhost),否则会直接抛出 SecurityError。开发时如果在 file:// 协议下打开 HTML 文件,也会失败——必须起一个本地服务(比如 python3 -m http.server)。
检查可用性要写成这样:
if ('showDirectoryPicker' in window) {
// 可用
} else {
alert('当前浏览器不支持选择文件夹');
}
调用 showDirectoryPicker 并获取文件夹句柄
它返回的是一个 FileSystemDirectoryHandle 对象,不是路径字符串,也不是 File 列表。这个句柄本身不能直接读取内容,必须用它的方法遍历或获取子项。
典型调用方式:
try {
const dirHandle = await window.showDirectoryPicker();
// 后续用 dirHandle.entries() 或 dirHandle.values() 遍历
} catch (err) {
if (err.name === 'AbortError') {
console.log('用户取消了选择');
} else {
console.error('选择文件夹失败:', err);
}
}
注意点:
-
showDirectoryPicker()是异步的,必须用await或.then() - 用户点击「取消」会抛出
AbortError,不是普通 rejection,别漏判 - 返回的
dirHandle没有name或fullPath属性,只有name(仅文件夹名,不含路径)
遍历文件夹里的文件和子文件夹
FileSystemDirectoryHandle 提供 entries()(返回迭代器)和 values()(只返回值)两种遍历方式。每个子项是 [name, handle] 形式的元组(entries())或单独的 FileSystemHandle(values())。你需要用 handle.kind 区分是文件还是目录:
示例:递归列出所有文件路径(相对目录名):
async function listAllFiles(dirHandle, path = '') {
const results = [];
for await (const entry of dirHandle.values()) {
const fullPath = path ? `${path}/${entry.name}` : entry.name;
if (entry.kind === 'file') {
const file = await entry.getFile();
results.push({ path: fullPath, size: file.size, type: file.type });
} else if (entry.kind === 'directory') {
results.push(...await listAllFiles(entry, fullPath));
}
}
return results;
}
关键细节:
- 必须用
for await遍历,因为entries()和values()返回的是异步迭代器 -
entry.getFile()才能拿到真正的File对象;只调entry本身拿不到内容 - 子目录需递归调用,不能靠一次
values()拿到深层文件 - 没有内置的「跳过 .git」「按扩展名过滤」逻辑,得自己加条件判断
常见报错和权限问题
最常遇到的是 NotAllowedError:通常是因为触发动作不是由用户直接手势(如 click)引起的。例如在 setTimeout、fetch 回调或 input 的 change 事件里调用,都可能被浏览器拒绝。
必须满足:
- 调用
showDirectoryPicker()的函数,必须是用户点击按钮等同步触发的事件处理器 - 不能在
Promise.then()、setTimeout、requestIdleCallback等异步回调中首次调用 - 如果页面被 iframe 嵌入,父页面需设置
allow="clipboard-read; clipboard-write; display-capture; geolocation; microphone; camera; encrypted-media; usb; serial; hid; filesystem-write;"(其中filesystem-write是必需的)
另外,用户第一次授权后,浏览器不会记住「永久允许」;每次刷新页面后再次调用仍会弹窗确认——这是设计使然,无法绕过。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











