webkitdirectory 属性仅在 Chrome、Edge 和 Safari 16.4+ 支持文件夹选择,Firefox 及旧版 Safari 不支持;需同时添加 directory 和 multiple 属性,通过 webkitRelativePath 过滤隐藏文件并手动注入路径信息至 FormData 才能保留目录结构。

用 webkitdirectory 触发文件夹选择,但不支持所有浏览器
HTML 原生支持文件夹上传的唯一方式是给 <input type="file"> 加上 webkitdirectory 属性:<input type="file" webkitdirectory directory>。这个属性会让 Chrome、Edge(基于 Chromium)、新版 Safari(macOS/iOS 16.4+)弹出“选择文件夹”对话框,而不是单个文件。
但它在 Firefox 和旧版 Safari(iOS
常见错误现象:
- 加了
webkitdirectory却点开还是选单个文件 → 检查是否漏了directory属性(二者需共存) - 控制台报错
NotSupportedError→ 当前浏览器不支持,比如 Firefox 或 Safari 15 - 选中文件夹后
event.target.files为空 → 没加multiple属性(webkitdirectory必须配合multiple才能读取子项)
FileList 里包含隐藏文件,得手动过滤
浏览器把整个文件夹结构扁平化展开进 FileList,包括 macOS 的 .DS_Store、Windows 的 Thumbs.db、Linux 的 .git 等。它们不是“被隐藏”,而是真实存在、有完整路径的 File 对象。
立即学习“前端免费学习笔记(深入)”;
过滤逻辑必须在 JS 里做,不能依赖后端或 accept 属性(它只对文件扩展名起作用,对路径名无效):
- 用
file.webkitRelativePath获取相对路径(如"myfolder/.DS_Store"),这是唯一能判断是否为隐藏项的字段 - 检查路径开头是否为
"."(注意:不是只看文件名,因为sub/.env也该被排除) - 别用
file.name.startsWith(".")—— 它会漏掉子目录下的隐藏文件
示例过滤代码:
const files = event.target.files;
const validFiles = Array.from(files).filter(file => {
const relPath = file.webkitRelativePath || file.name;
return !relPath.split('/').some(part => part.startsWith('.'));
});
上传时用 FormData 附带路径信息,否则后端收不到层级
原生 webkitdirectory 提交的 FormData 默认只传文件内容,不传路径。如果后端需要还原目录结构(比如保存为 /uploads/project/src/main.js),你得手动把 webkitRelativePath 作为额外字段塞进去。
否则后端收到的全是同名文件(比如多个 index.html),或只能存成扁平列表。
- 不要只调用
formData.append("file", file)—— 这会丢失路径 - 改用
formData.append("file", file, file.webkitRelativePath || file.name),第三个参数是自定义文件名,可保留路径 - 后端 multer 等库能解析这个路径字段,Node.js 中可用
req.file.originalname读到它
iOS 16.4+ 支持但有限制:无法访问 iCloud 同步文件夹
iOS 16.4 确实加入了 webkitdirectory,但实际体验受限:
- 只能选择“本地存储”的文件夹,iCloud Drive 里的文件夹点不开(系统直接禁用)
- 相册、备忘录、Pages 文档等沙盒内数据不可见,不属于“文件 App”暴露的目录树
- 用户首次使用时会弹窗请求“完全访问文件”,若拒绝,
webkitdirectory会静默失效(files.length === 0)
所以 iOS 上真正能用的场景很窄:用户事先把文件夹通过“文件 App”拷贝到“我的 iPhone”根目录下,再从那里选。生产环境建议加一层提示:“请先将文件夹复制到‘我的 iPhone’,再尝试上传”。



















