HTML通过webkitdirectory和directory属性实现文件夹选择,配合multiple上传多个带webkitRelativePath的File对象,并发控制在3–5个以避免阻塞,服务端依relativePath还原目录结构。

HTML 本身不支持直接上传整个文件夹——但通过 webkitdirectory 和 directory 属性,可以让用户选择文件夹,浏览器会递归列出其内所有文件(含子目录路径),再配合并发上传逻辑,就能实现实质上的“文件夹上传加速”。关键不在“一次传一个文件夹”,而在“一次选中 N 个文件 + 并发发出去”。
怎么让支持文件夹选择(Chrome/Firefox)
必须同时用两个属性,缺一不可,否则行为不一致或完全失效:
-
webkitdirectory:Chrome、Edge 等 WebKit/Blink 内核必需,启用原生文件夹选择对话框 -
directory:Firefox 专用,没有它 Firefox 就只允许单文件 -
multiple:必须带上,否则即使选了文件夹,e.target.files也只返回第一个文件
示例 HTML:
<input type="file" id="folderUpload" webkitdirectory directory multiple>
注意:Safari 不支持这两个属性,IE 已淘汰;移动端(iOS/Android)基本不支持——这不是兼容性“优化项”,而是硬性限制,得提前告知用户。
为什么不能直接用 FormData.append('folder', folder)?
因为没有 Folder 类型的 DOM 对象。浏览器暴露给 JS 的只有 File 实例列表,每个 File 都带 webkitRelativePath(如 "docs/spec.pdf" 或 "parts/bracket.step")。你看到的是“文件夹”,JS 拿到的其实是“一堆带路径前缀的文件”。
常见错误:
- 漏过滤 macOS 隐藏文件:
__MACOSX、.DS_Store,它们也会出现在files列表里 - 误把
webkitRelativePath当作本地绝对路径去读取(实际不可读,纯只读元数据) - 没做路径合法性校验,用户手动改 DOM 注入
../etc/passwd类路径(虽然后端应拦截,但前端可提前筛掉含..的路径)
怎么控制并发数并避免浏览器卡死?
并发不是越多越好。Chrome 默认对同一域名最多 6 个 HTTP/1.1 连接;超过后请求排队,反而更慢。建议设为 3–5 个并发较稳妥。
核心逻辑是「队列 + 计数器」,不用 Promise.all 打包全部,否则失败一个就全崩:
- 把
e.target.files转成数组,过滤掉非法路径后存入待上传队列uploadQueue = [...] - 维护一个
activeCount = 0和最大值MAX_CONCURRENT = 4 - 写一个
startNextUpload()函数:仅当activeCount 0时才取一个文件发起 XHR - 每个 XHR 的
xhr.onload / xhr.onerror里执行activeCount--并再调一次startNextUpload()
别用 fetch 做并发上传进度——它不支持 upload.onprogress,你要实时显示每个文件的上传百分比,只能靠 XMLHttpRequest。
服务端接收时怎么还原目录结构?
前端无法“传一个 zip 包”再让后端解压——那样就丢失了单文件进度和断点续传能力。正确做法是:每个 File 单独发一个请求,并在 FormData 里显式带上路径字段:
const formData = new FormData();<br>formData.append('file', file);<br>formData.append('relativePath', file.webkitRelativePath); // 如 "assembly/cover.dwg"
后端收到后,根据 relativePath 拼出完整保存路径(如 /uploads/project_x/assembly/cover.dwg),再确保目录存在(mkdir -p 或等价逻辑)。
容易被忽略的一点:多个文件可能有相同 relativePath(比如同名文件在不同子目录被重命名覆盖),但前端不会报错,服务端必须做冲突处理(加时间戳、UUID 后缀,或直接 409 返回让用户确认)。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











