现代浏览器支持直接上传整个文件夹且不刷新页面,需同时使用webkitdirectory和directory属性配合FormData及fetch或XMLHttpRequest;input.files返回的FileList已包含子文件夹所有文件并保留相对路径(如"project/src/index.js"),通过file.webkitRelativePath获取,上传时须将该路径作为form.append第三参数传给服务端,并做好路径净化防攻击。

能直接上传整个文件夹,且不刷新页面——现代浏览器(Chrome、Edge、Firefox 97+、Safari 16.4+)完全支持,但必须同时满足两个条件:webkitdirectory 属性 + FormData 配合 fetch 或 XMLHttpRequest。IE 和旧版 Safari 不行,别试。
用 input 正确触发文件夹选择
关键不是加 multiple,而是加 webkitdirectory 和 directory(后者是标准属性,前者是历史兼容):
<input type="file" id="folder-input" webkitdirectory directory multiple>
常见错误现象:
-
directory单独用在 Firefox 中无效,必须搭配webkitdirectory - 加了
accept会禁用文件夹选择(比如accept="image/*"),浏览器直接忽略directory - 用
display: none隐藏后调click(),多数浏览器拒绝触发(安全限制),必须靠用户真实点击
正确做法:用 <label></label> 包裹,或透明覆盖按钮:
立即学习“前端免费学习笔记(深入)”;
<label for="folder-input">选择文件夹</label><input type="file" id="folder-input" webkitdirectory directory multiple style="position: absolute; opacity: 0;">
递归读取子目录里的所有文件
input.files 返回的 FileList 在启用 directory 后,**已包含子文件夹内所有文件,并保留完整路径(如 "project/src/index.js")**,无需手动遍历目录树。
但注意:它只返回文件(File 对象),不返回空文件夹本身;路径字符串可通过 file.webkitRelativePath 获取(所有支持该功能的浏览器都提供此属性)。
实操建议:
- 遍历时不要依赖
file.path(不存在),只用file.webkitRelativePath - 若需过滤,按路径字符串判断即可:
if (file.webkitRelativePath.endsWith('.jpg')) { ... } - 大文件夹(上千文件)下,
input.files可能延迟几秒才就绪,别在change后立刻遍历,加个setTimeout(..., 0)更稳
用 FormData 构建带路径信息的异步请求
后端要还原目录结构,就必须把 file.webkitRelativePath 作为字段一起传过去,不能只传 file 本身。
示例代码(fetch 方式):
document.getElementById('folder-input').addEventListener('change', async (e) => {<br> const files = Array.from(e.target.files);<br> const form = new FormData();<br> for (const file of files) {<br> form.append('files', file, file.webkitRelativePath); // 第三个参数设为路径,服务端收到的就是该路径名<br> }<br> const res = await fetch('/upload-folder', { method: 'POST', body: form });<br> console.log(await res.json());<br>});
关键点:
-
form.append('files', file, file.webkitRelativePath)中第三个参数决定服务端接收到的文件名(含路径),否则默认是原始文件名,丢失结构 - 别用
JSON.stringify包一层再传 ——FormData不支持嵌套对象,后端收不到 - 如果后端框架(如 Express + multer)默认只解析顶层字段,需确认它是否支持
files字段接收多个同名文件并保留原始文件名
为什么不用 iframe?
iframe 方案(target + hidden iframe)在文件夹上传场景下基本不可用:
- 表单提交时,
input[type="file"][webkitdirectory]的files是动态生成的,iframe 提交无法自动携带全部文件(浏览器只提交被选中的那个“文件夹入口”,不递归) - 服务端从 iframe 请求里只能拿到一个空或无效的文件字段,因为
enctype="multipart/form-data"在 iframe 模式下对多文件+路径信息的支持极差 - 即使硬塞,也无法把
webkitRelativePath作为额外字段发出去 —— 表单没有机制让自定义字段和每个文件一一绑定
所以,只要目标浏览器明确支持 HTML5 File API(即你不需要兼容 IE 或 Safari
最易被忽略的一点:服务端接收到的每个文件名是完整路径(如 "src/main.js"),写入磁盘前必须做路径净化,防止 ../../../etc/passwd 类攻击 —— 浏览器传来的 webkitRelativePath 是用户可控的,不能直接拼进 fs.writeFile。



















