正确上传文件夹需用,利用webkitRelativePath还原结构;超时源于前端、网关、后端三处配置及未分片导致的内存/线程阻塞,须分片+并发控制+断点续传+服务端路径校验。

HTML 本身不能直接上传文件夹,但现代浏览器支持 webkitdirectory 属性,可触发原生文件夹选择;网络超时不是 HTML 的问题,而是上传逻辑、分片策略和后端配置共同决定的。
怎么用 input 正确选中整个文件夹
必须用带 webkitdirectory 和 directory 的 <input type="file">,且不能省略 multiple —— 否则 Chrome/Safari 只返回根目录下的文件,不递归子目录。
常见错误写法:<input type="file" webkitdirectory>(缺 directory 和 multiple)
正确写法:
立即学习“前端免费学习笔记(深入)”;
<input type="file" id="folderInput" webkitdirectory directory multiple>
注意:webkitdirectory 是非标准属性,仅在 Chromium(Chrome/Edge)、Safari 有效;Firefox 不支持,需 fallback 到 ZIP 手动压缩 + 文件上传。
选中后,e.target.files 中每个 File 对象都带 webkitRelativePath 字段,能还原原始层级结构,比如 "src/js/main.js" 或 "assets/images/logo.png"。
为什么上传大文件夹总卡在“网络超时”
超时通常发生在三处:前端请求超时、Nginx/Apache 网关超时、后端服务(如 Spring Boot)读取流超时。单靠改 fetch 的 signal.timeout 没用,因为真正耗时的是分片上传和合并过程。
生成可在 deepbi 沙箱 iframe 内运行或作为独立文件运行的交互式 HTML 页面,使用 `window.deepbi` JS SDK 实现数据查询、空间管理、文件浏览及用户操作。当用户提出以下需求时启用该能力: - 创建 KPI 仪表盘、数据表格、文件浏览器、成员列表、管理页面; - 明确要求“转成 dhtml”、“转译成 HTML”、“把 XX 页面转成……”、“控制台页面”、“管理后台”、“DeepBI 页面迁移”; - 提及位于 `apps/console/src/featur
关键点:
-
fetch默认无超时,但若你手动加了AbortSignal.timeout(30_000),30 秒内没收到响应就会中断 —— 这对几百 MB 的文件夹完全不够 - Nginx 默认
client_max_body_size 1m、proxy_read_timeout 60,不调大会直接 413 或 504 - Spring Boot 的
spring.servlet.multipart.max-request-size和server.tomcat.connection.connection-timeout都要同步放宽 - 更隐蔽的问题:前端未分片,一次性把整个文件夹所有文件塞进一个
FormData发送 —— 浏览器内存爆掉或主线程卡死,看起来就像“超时”
如何避免超时:必须分片 + 并发控制 + 断点续传
文件夹上传不能当单个大文件处理,得按文件粒度分片,每个 File 单独走上传流程,并记录已传成功的 path 和 etag(或 MD5)。
实操建议:
- 遍历
e.target.files,对每个File计算file.webkitRelativePath,作为服务端存储路径依据 - 对单个大文件(>5MB)启用分片:用
file.slice(start, end)切块,每块 2–5MB,带上chunkIndex、totalChunks、fileId(可用file.name + file.lastModified做简易 hash) - 并发数设为 2–3,别盲目开 10 个 —— 太多并发反而触发浏览器限流或后端连接池耗尽
- 每个分片请求加唯一
X-Upload-IDheader,后端据此做幂等和断点识别;失败后重试前先查该 ID 是否已存在完整分片 - 禁用
fetch自动重定向(redirect: 'manual'),防止 302 跳转干扰状态判断
示例关键逻辑片段:
const uploadChunk = async (file, chunkIndex, totalChunks) => {
const chunk = file.slice(chunkIndex * CHUNK_SIZE, (chunkIndex + 1) * CHUNK_SIZE);
const formData = new FormData();
formData.append('file', chunk);
formData.append('path', file.webkitRelativePath); // 保留结构
formData.append('chunkIndex', chunkIndex);
formData.append('totalChunks', totalChunks);
<p>const res = await fetch('/api/upload/chunk', {
method: 'POST',
body: formData,
// 不设 headers!FormData 会自动生成正确的 Content-Type
});
return res.json();
};容易被忽略的细节:路径合法性与服务端校验
webkitRelativePath 是浏览器提供的字符串,**不可信**。用户可伪造或通过 DevTools 修改它,导致路径穿越(如 "../../etc/passwd")或注入非法字符(../、\0、空格、Unicode 控制符)。
前端能做的有限,重点在服务端:
- 收到
path后,用path.normalize()(Node)或Paths.get().normalize()(Java)标准化,再检查是否仍以允许前缀开头(如"uploads/") - 拒绝含
".."、"//"、控制字符的路径 - 对每个上传的文件,校验
Content-Type是否匹配扩展名,防止 .html 伪装成 .png - 文件夹上传完成前,所有分片应暂存临时目录,合并成功后再 mv 到正式位置 —— 避免半成品暴露
真正的难点不在“怎么传”,而在“怎么安全、可控、可观测地传完”。超时只是表象,背后是上传状态管理、错误恢复、路径治理三者的耦合。没做分片和断点的文件夹上传,在生产环境基本不可用。


















