浏览器上传大文件崩溃是因内存耗尽:读取1gb文件可能占用2gb内存,超出chrome js堆限制(≤2gb),触发oom致白屏;uploaddir通过服务端目录监听机制绕过该限制,客户端仅发起轻量握手请求,后续用rsync等工具直传磁盘,零前端内存压力。
浏览器直接上传大文件为什么会崩溃
因为浏览器把整个文件读进内存再发请求,1GB 文件可能吃掉 2GB 内存(含 Base64 编码或 Blob 处理开销),加上 Chrome 对单个 JS 堆的默认限制(通常 ≤2GB),FileReader 或 f.readAsArrayBuffer() 过程中直接触发 OOM,页面白屏或自动关闭标签页。
- 不是网络超时,是内存耗尽 —— 查看 Chrome 任务管理器(Shift+Esc)能看到“JavaScript 内存”列飙升后归零
-
fetch()或XMLHttpRequest本身不缓存全量数据,但前端读取逻辑(比如分块前先file.slice()太多)会提前触发加载 - 即使加了进度条,也掩盖不了内存已在后台暴涨的事实
UploadDir 是什么,为什么它能绕过浏览器限制
UploadDir 不是前端 API,而是某些后端框架(如 Go 的 gin-contrib/fileupload、Python FastAPI 配合 shutil.copyfileobj() 流式接收)提供的服务端目录监听机制:客户端只发一个轻量级请求(比如 POST /upload/start),后端生成临时上传路径,然后你用 scp、rsync 或本地脚本把大文件直接写入该目录,服务端定时扫描并触发处理逻辑。
- 完全跳过 HTTP body 解析和内存缓冲,文件以原始字节流落地磁盘
- 对浏览器零压力 —— 它只负责发起一次握手,后续传输由系统工具完成
- 注意:
UploadDir不是标准协议,具体实现依赖后端代码,常见于内部工具或定制平台(如某些私有 Git LFS 替代方案)
怎么安全启用 UploadDir 模式(关键配置点)
核心不是“怎么调用”,而是“怎么不让它变成任意文件写入漏洞”。必须收口在可控路径 + 明确生命周期 + 权限隔离。
- 上传目录必须是绝对路径且不在 Web 根目录下,例如
/var/tmp/upload_202410/,禁止使用./uploads或../static - 每个上传任务应带唯一 token,目录名包含该 token(如
/var/tmp/upload_202410/abc123/),避免撞名覆盖 - 后端需设置最大单文件大小(通过
os.Stat()或du -h定期检查)、过期时间(比如 24 小时未完成则rm -rf) - Linux 下建议用独立用户运行服务,并给上传目录设
chown uploaduser:uploadgroup /var/tmp/upload_202410和chmod 750
前端要改什么?其实几乎不用改
你只需要替换掉原来绑在 input[type="file"] 上的 onChange 处理函数。新流程里,浏览器只做三件事:申请 token、展示本地命令、轮询状态。
fetch('/upload/start', { method: 'POST' })
.then(r => r.json())
.then(({ token, upload_dir }) => {
// 告诉用户执行:
// rsync -avP ./large.zip user@server:/var/tmp/upload_202410/${token}/
document.getElementById('cmd').textContent =
`rsync -avP ./large.zip user@server:${upload_dir}`;
});
- 不需要改任何
FormData、AbortController或分片逻辑 - 轮询用
fetch(`/upload/status?token=${token}`)即可,返回{ status: "ready" | "processing" | "done" } - 别忘了禁用原上传按钮,防止用户误点两次 ——
button.disabled = true要在fetch后立刻执行











