webkitdirectory不能递归上传子文件夹,仅获取所选文件夹的直接子文件,忽略所有子目录;要实现真正递归需用showdirectorypicker()手动遍历,但该api仅chrome/edge支持且需https和用户授权。

webkitdirectory 能否递归上传子文件夹
不能。webkitdirectory 仅支持选择一个文件夹及其**直接子文件**,所有子文件夹本身会被当作目录项忽略,不会递归读取其内部内容。
这是 WebKit/Blink 内核的明确行为限制,不是浏览器 bug,也不是配置问题。Chrome、Edge、Safari(macOS)均如此——选中 foo/,得到的是 foo/file1.txt、foo/image.png,但 foo/sub/ 这个目录对象不会触发任何文件读取,也不会出现在 input.files 列表中。
-
input.files返回的FileList里只含文件,不含Directory对象 - 即使用户手动勾选了嵌套文件夹(某些系统 UI 允许),浏览器仍会过滤掉所有
kind === "directory"的条目 - 没有标准 API 可通过
webkitdirectory触发多层遍历
想真正递归上传必须自己实现目录遍历
要拿到 foo/sub/nested.txt,必须用 webkitdirectory 获取顶层目录后,再借助 FileSystemDirectoryHandle(即 File System Access API)手动遍历。
注意:这不是 webkitdirectory 的延伸能力,而是完全独立的新路径,且有严格前提:
- 仅在 HTTPS 或 localhost 下可用
- 需用户主动授予目录访问权限(首次调用
showDirectoryPicker()会弹框) - Chrome 86+、Edge 86+ 支持;Firefox 和 Safari 不支持该 API
示例关键逻辑:
const dir = await window.showDirectoryPicker();
const files = [];
for await (const entry of dir.values()) {
if (entry.kind === 'file') {
const file = await entry.getFile();
files.push(new File([await file.arrayBuffer()], `${dir.name}/${entry.name}`, { type: file.type }));
} else if (entry.kind === 'directory') {
// 递归处理子目录(需自己写遍历函数)
}
}
为什么不用 webkitdirectory + FileReader 模拟递归
因为做不到。根本原因是:webkitdirectory 提交的 FileList 里压根不包含任何子目录下的文件,你连起点都没有。
常见误解是“把整个文件夹拖进 input 就能自动展开”,但实际行为取决于浏览器和操作系统交互逻辑——macOS Finder 拖拽文件夹到 <input type="file" webkitdirectory> 时,Chrome 仍只提取顶层文件;Windows Explorer 同样如此。
- 尝试用
FileReader读取File对象以外的路径?不行,File接口不暴露路径字段,且无法构造跨目录的File - 试图用
URL.createObjectURL()绕过?它只对已有File有效,对目录无效 - 依赖第三方库如
dropzone?它们底层同样受限于input.files的内容,无法突破浏览器沙箱
兼容性差 + 权限门槛高,实际项目怎么选
如果目标用户主要是现代 Chrome/Edge 且能接受权限提示,优先用 showDirectoryPicker() + 递归遍历;否则,老老实实引导用户压缩成 ZIP 上传,后端解压——这是目前最稳定、兼容性最好的方案。
额外提醒:别在 webkitdirectory 上浪费时间调试“为什么 sub/sub2 文件没进来”,它本来就不该进来。重点检查是否误以为它等价于本地命令行的 find . -type f。











