首页 > Technology > 正文

前端大文件断点续传:从 0 写通

fenij 2026-09-16 2 Technology

大文件直传基本都会翻车:反向代理 60 秒超时、浏览器把整文件读进内存、传到 95% 断网就得从头再来。要稳,得把文件切成片、一片一片传、断了只补没传完的那几片。下面这套分片 + 续传实现,我在一个 500MB 视频上传需求里踩过坑后定型,前端切片、续传询问、服务端合并的代码都能直接抄。

直传为什么必败

标准 multipart 一次 POST 整个文件,问题集中在三处:代理有请求体大小和时间限制、浏览器会整文件加载进内存、失败只能重头传。把这三处量化对比一下就清楚:

维度 一次直传 分片续传
超时风险 受反向代理 body 限制,常 60–300s 一刀切 单分片体积小,几乎不超时
内存占用 整文件塞进 ArrayBuffer,移动端直接崩 只持切片引用,不占内存
断网代价 从 0 重来 只补缺失分片

结论很直接:文件超过几十 MB,就别直传。

分片上传的核心思路

浏览器用 Blob.slice() 把文件切成固定大小块,每块带序号和会话 ID 单独请求,服务端按序号落盘。关键是【标识符】——用文件名 + 大小,或内容 hash 生成,让服务端能认出”这是同一个半截上传”,断网回来还能接着传。

切片前端代码:

const CHUNK = 5 * 1024 * 1024; // 5MB 一片
const total = Math.ceil(file.size / CHUNK);
const id = file.name + '-' + file.size; // 简化标识符
for (let i = 0; i < total; i++) {
  const blob = file.slice(i * CHUNK, (i + 1) * CHUNK);
  const fd = new FormData();
  fd.append('chunk', blob);
  fd.append('index', i);
  fd.append('id', id);
  await fetch('/upload', { method: 'POST', body: fd });
}

断点续传怎么落地

续传的本质是”上传前先问服务端:你收到第几片了”。这里用 XMLHttpRequest 而不是 fetch,因为 xhr.upload.onprogress 能在传输途中回报字节进度,进度条才顺滑;fetch 要等响应回来才知道结果,慢连接下会卡 30 秒没动静。

async function uploadWithResume(file, id) {
  const res = await fetch(`/upload/status?id=${id}`);
  const done = new Set(await res.json()); // 已收分片序号
  for (let i = 0; i < total; i++) {
    if (done.has(i)) continue;            // 已传的跳过
    await sendChunk(file, id, i);         // 只传缺失的
  }
  await fetch('/upload/merge', {
    method: 'POST',
    body: JSON.stringify({ id })
  });
}
1
初始化会话
2
逐片上传
3
断网捕获
4
问状态补传
5
合并校验

踩坑实录

合并文件时我图省事用 fs.readdirSync 读目录后直接排序拼接,结果 chunk_1、chunk_10、chunk_11 按字符串字典序排成了 1→10→11→2,文件全乱、解压直接报错。改成按数值 parseInt(index) 升序再 appendFileSync 才正常。一句教训:分片名带数字,永远按数值排序,别信字符串排序。

选型:自研还是直接上 tus

评测结论
推荐对外大文件上传,直接用 tus 协议(tus-js-client),自带断点、校验、并行分片,少踩一半坑。
不推荐业务早期手搓一套”看似够用”的续传,边界 case(并发乱序、中断重入)会咬人。
仅适合内部小工具、服务端完全可控的环境,才值得自研分片逻辑。

常见问题

分片大小设多少合适?

1–10MB 是甜区。太小请求数爆炸、太大单分片重试成本高;移动端网络不稳可降到 256KB–1MB。

并发上传分片会更快吗?

理论上会,但 Node 单进程 + 磁盘顺序写时并发反而乱序、合并易错。先顺序跑通,再按服务端能力开 2–3 路并发。

怎么防止重复文件重复传?

标识符用内容 hash(如 SparkMD5),服务端先查该 hash 是否已存在,命中就秒传——这就是常说的”极速上传”。

你在大文件上传上还踩过什么坑?欢迎在 fenij.com 留言聊聊,我整理成下一篇。

相关完整手册

系统化的排查与配置思路,建议顺手收藏这几篇完整手册: