webkitdirectory 属性仅 Chrome/Safari 支持,Firefox 需用标准 directory,IE/旧 Edge 不支持;files 列表扁平无目录结构,仅靠 webkitRelativePath 字符串还原路径;需手动判空、权限校验及后端 MIME 校验。

webkitdirectory 属性只在 Chrome/Safari 有效,Firefox 要用 directory
浏览器对文件夹上传的支持并不统一。webkitdirectory 是 Chrome 和 Safari 的私有属性,Firefox 则要求用标准的 directory。两者必须同时写,否则 Firefox 下完全不触发文件夹选择:
<input type="file" webkitdirectory directory multiple>
IE 和旧版 Edge 完全不支持,连降级提示都得靠 JS 检测:navigator.userAgent.includes('Chrome') 或更稳妥地判断 'webkitRelativePath' in files[0]。别指望用 CSS 或 polyfill 补这个缺口——底层 File API 就没暴露目录结构能力。
files 列表是扁平的,没有嵌套目录结构
选中 /project/src/index.js 和 /project/README.md,你拿到的 FileList 里只有两个 File 对象,它们的 webkitRelativePath 字段分别是 "project/src/index.js" 和 "project/README.md"。但这个路径只是字符串,不是真实目录树。
- 不能靠
files[0].path获取父目录对象 - 不能用
files.filter(f => f.path.startsWith('src/'))后直接递归上传子目录(因为根本没有子目录) - 后端重建目录时,必须自己按
/分割路径、逐级创建文件夹
常见错误是误以为 files 包含 isDirectory 或 children 属性——它没有。
立即学习“前端免费学习笔记(深入)”;
用户取消选择或选空文件夹时,change 事件仍会触发
change 事件不区分“选中了”还是“点了取消”,只要弹出过文件选择框就触发。此时 e.target.files.length === 0,但代码若没判空,后续遍历会报错。
正确做法是加一层守卫:
input.addEventListener('change', e => {<br> if (!e.target.files.length) return;<br> const files = Array.from(e.target.files);<br> // 后续处理<br>});
另外注意:即使用户选了文件夹,也可能因权限问题(比如 macOS 的“完整磁盘访问”未开启)导致部分子文件不可读,files 列表里只会包含可访问的那些——这个行为无提示、不可捕获,只能靠后端校验总数是否匹配。
accept 属性对文件夹无效,类型过滤得靠 JS 手动筛
accept=".jpg,.png" 这类写法在文件夹上传中完全不起作用。浏览器不会过滤掉文件夹里的非图片文件,files 列表照单全收。
真正可行的过滤方式只有两种:
- 前端 JS 遍历
files,用file.type或file.name.split('.').pop().toLowerCase()做白名单匹配 - 后端强制校验每个文件的 MIME 类型(用
finfo_open(FILEINFO_MIME_TYPE)等),不能信前端传来的任何字段
尤其要注意图片马(如 GIF89a 开头 + PHP 代码):前端 file.type === 'image/gif' 为真,但后端用魔术字节检测才能识破。



















