webkitdirectory 是唯一原生支持文件夹上传的方案,仅 Chromium 内核浏览器有效;需同时声明 type="file"、webkitdirectory、directory、multiple 四个属性,且依赖 webkitRelativePath 获取层级信息,但空目录不被暴露,须前端主动补全路径元数据。

用 webkitdirectory 属性是唯一原生方案,但只在 Chrome/Edge 有效
浏览器原生支持文件夹上传,靠的就是 input 标签的 webkitdirectory 属性。它能让用户点选一个文件夹,然后 JavaScript 拿到里面所有文件(包括子目录里的),且每个 File 对象会带一个 webkitRelativePath 属性——这才是层级信息的唯一来源。
常见错误是以为加了 multiple 就能保留结构,其实不行:multiple 只是多选文件,路径全丢;webkitdirectory 才触发递归遍历,并注入相对路径。
- 必须写成
<input type="file" webkitdirectory directory multiple>,四个属性缺一不可(directory是 HTML5 标准属性,webkitdirectory是实际起效的兼容写法) -
webkitRelativePath值形如"project/src/main/java/Controller.java",不是绝对路径,也不含盘符或根斜杠,可直接传给后端作为目标路径依据 - Firefox 和 Safari 完全不支持该属性;IE 已淘汰;只有 Chromium 内核浏览器(Chrome、Edge、国产双核)可用
webkitRelativePath 拿不到?检查这三个地方
即使用了 webkitdirectory,也常出现 webkitRelativePath 为空或为 ""。这不是代码写错了,而是被浏览器或系统拦截了:
- 页面必须通过 HTTPS 或
localhost访问;HTTP 站点下该属性会被静默清空 - 用户不能手动拖拽文件夹到页面(哪怕监听了
drop事件),只有通过click触发input弹窗选中才生效 - 某些 Linux 桌面环境(如 GNOME 的 Nautilus)或 macOS 的 Finder 在特定版本下会截断路径,表现为只返回文件名、无目录层级——换用 Chrome 最新版或改用 KDE/Dolphin 可缓解
后端怎么安全还原目录结构?别直接拼 fs.mkdirSync(path)
前端传来的 webkitRelativePath 是用户可控输入,直接用于服务端文件系统操作等于开门揖盗。比如传个 "../../../etc/passwd" 就可能越权写入。
立即学习“前端免费学习笔记(深入)”;
真正安全的做法是:把路径拆成数组,逐段校验,再拼成白名单内的合法路径。
- 先用
path.posix.normalize()(Node.js)或os.path.normpath()(Python)标准化,消除..和重复斜杠 - 拆分后检查每一段是否匹配正则
/^[a-zA-Z0-9._-]+$/,拒绝含控制字符、Unicode 零宽空格、路径遍历符号的片段 - 限定最大深度(如不超过 8 层)、最长总长度(如不超过 255 字符),防止超长路径导致
ENAMETOOLONG - 最终写入时,用
path.join(uploadRoot, safeSegments.join('/')),永远以你指定的根目录为起点
空文件夹怎么办?原生 API 根本不暴露它们
这是百万级目录结构上传里最隐蔽的坑:webkitdirectory 只返回有文件的路径节点,空目录(比如 "logs/" 下什么都没有)完全不会出现在 FileList 里。后端收不到任何信号,自然无法创建对应目录。
目前没有浏览器原生补救方式,只能前端主动“补全”:
- 上传前用
FileSystemDirectoryHandle(仅 Chromium)递归遍历整个文件夹树,生成一份完整路径清单(含空目录),和文件一起打包成 JSON 元数据传给后端 - 如果不用
FileSystemAccessAPI(兼容性差),就退回到 ZIP 方案:前端用JSZip把整个文件夹打成 ZIP,连同目录树 JSON 一起上传;后端解压 + 按 JSON 创建空目录 - 千万别指望后端“扫描并补齐”,因为上传是异步、分片、并发的,不同文件到达时间不同,无法可靠推断某目录是否“本该存在但还没来”
空目录缺失问题最容易被忽略,直到上线后用户反馈“我明明上传了 config/ 和 scripts/ 两个空文件夹,结果系统里只剩 scripts/ 下的文件”,这时候再补逻辑代价很大。从第一版就该把目录树元数据当成必传字段,而不是当作可选优化。



















