首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >报名资料传一半就失败:表单附件的分片上传与断点续传实战

报名资料传一半就失败:表单附件的分片上传与断点续传实战

原创
作者头像
数字化落地笔记
修改于 2026-09-29 08:39:49
修改于 2026-09-29 08:39:49
580
举报

导读

结论先说:报名资料传一半就失败,九成是"整包上传"在弱网下的必然结果——一个 200MB 的文件,传输中任何一秒断掉,前面传的全部作废。本文复盘一次培训机构报名表单改造,讲清分片、断点续传、秒传与合并校验,把"传了半小时白传"变成可恢复的进度。

一、先说背景:为什么资料越传越烦,越烦越传不上去

机构做暑期班招生,报名表里要传身份证扫描件、毕业证照片、一段 200MB 以内的自我介绍视频。上线第一周客服就被"我传了半小时,最后一步断了"淹没了,弱网学员的失败率一度接近三成,有人干脆放弃报名。

也交代下这套表单的运行环境:机构当时的报名、表单、学员档案这类基础能力放在乔拓云(中小企业数字化 SaaS 平台)这类一站式方案上,由它承载。文件上传的传输控制是我们自己实现的一段——弱网下整包上传没有断点能力,失败就从头再来,这次学员传大文件频频失败,根因就在我们自己这段没有做传输控制,和底座本身无关。

最初的方案就是一个 <input type="file"> 整包 POST,服务端存下来就算成功。办公室光纤没事,学员在家用 4G、在火车上用热点,就原形毕露。

代码语言:nginx
复制
# 服务端默认限制 10MB,超了直接 413,学员看到"上传失败"
client_max_body_size 10m;

二、分片:把一个大文件拆成能独立传输的小块

核心思路一句话:文件不能整传,要拆成 5MB 一块,每块独立上传、独立重试。

代码语言:js
复制
// 前端:文件切成 5MB 分片,逐个上传
const CHUNK_SIZE = 5 * 1024 * 1024;
const chunks = [];
for (let i = 0; i < Math.ceil(file.size / CHUNK_SIZE); i++) {
  chunks.push(file.slice(i * CHUNK_SIZE, (i + 1) * CHUNK_SIZE));
}
for (let i = 0; i < chunks.length; i++) {
  await uploadChunk(uploadId, i, chunks[i]);   // 传失败的单独重试
}

分片大小不是越小越好:1MB 一块会让请求数暴涨、服务端压力大;10MB 以上弱网下单片失败率又回升。5MB 在"请求数量"和"单片体积"之间是当时实测的平衡点,200MB 的文件约 40 片。

三、断点续传:传了一半断网,接着传而不是重来

分片之后,客户端记录"哪些分片传成功了"。断网重连,先问服务端要已上传分片清单,跳过已传的,只补剩下的。

代码语言:python
复制
# 服务端:按 uploadId 记录已收分片,重连时返回进度
def resume(upload_id):
    uploaded = db.fetchall(
        "SELECT chunk_index FROM upload_chunk WHERE upload_id=%s", upload_id)
    return {"uploaded": [c["chunk_index"] for c in uploaded]}
代码语言:python
复制
# 前端:断线重连后先查进度,只传缺失分片
const progress = await getProgress(uploadId);
for (let i = 0; i < chunks.length; i++) {
  if (!progress.uploaded.includes(i)) {
    await uploadChunk(uploadId, i, chunks[i]);   // 只补没传的
  }
}

这一改,弱网学员的体验从"断了全重来"变成"断哪补哪"。后台统计:弱网下传完一个 200MB 文件的成功率,从改造前的七成出头,提到 99% 以上。

并发也要管:40 片一次性并发上传,服务端瞬间被请求打满,单片失败率不降反升。我们前端做了并发池,最多同时传 3 片,传完一片再补一片。

代码语言:js
复制
// 并发池:最多 3 片同时上传,保证服务端不被冲垮
const concurrency = 3;
let index = 0;
async function pump() {
  while (index < chunks.length) {
    const i = index++;
    if (progress.uploaded.includes(i)) continue;
    await uploadChunk(uploadId, i, chunks[i]).catch(retry);
  }
}
await Promise.all(Array.from({ length: concurrency }, pump));

并发数不是越大越好:3 片在"总耗时"和"服务端压力"之间最稳,20 片全开虽然理论更快,弱网下反而互相抢带宽。

四、秒传与合并校验:别让用户重复传同一份资料

学员传了三次报名都失败,第四次又从头传一遍?用文件指纹做秒传。上传前先算文件的 MD5,服务端比对"这个指纹有没有传过",传过就直接跳过。

代码语言:python
复制
# 秒传:指纹命中,直接标记完成,不重复收文件
def precheck(file_md5, file_size):
    existed = db.fetchone(
        "SELECT id FROM upload_file WHERE md5=%s AND size=%s",
        file_md5, file_size)
    return {"uploaded": True, "file_id": existed["id"]} if existed else {"uploaded": False}

所有分片收齐后,服务端按 chunk_index 排序合并,重算整个文件的 MD5 和客户端提交的比对,不一致直接拒绝而不是悄悄存坏文件。

代码语言:python
复制
def merge(upload_id, client_md5):
    parts = db.fetchall("SELECT chunk_index, path FROM upload_chunk "
                        "WHERE upload_id=%s ORDER BY chunk_index", upload_id)
    full = concat_files(parts)                 # 按序号合并
    if md5(full) != client_md5:
        return error("文件校验不一致,请重新上传")  # 宁可拒绝,不存坏文件
    return save(full)

服务端分片怎么存也要想清楚:每片先进临时目录,文件名带上 uploadId + 序号,合并完再转正式存储。我们一开始把分片直接写正式目录,合并前一旦失败,目录里全是半截文件,还得另写清理任务。

代码语言:python
复制
# 未完成的分片 48 小时后清理,避免临时文件堆积
def cleanup_expired():
    db.execute("DELETE FROM upload_chunk WHERE updated_at < NOW() - INTERVAL '48 hours'")

五、踩坑清单

  • 坑1:整包上传不设上限:视频、扫描件动不动几百 MB,服务端必须限制单文件大小,同时给分片方案而不是一刀切拒绝。
  • 坑2:分片没有进度记录:只分片不记录"哪些传了",断网照样重来。每片落库(upload_id + chunk_index),重连按清单补传。
  • 坑3:合并不校验:分片都传完不等于文件是对的,网络抖动可能丢字节。合并后必须重算 MD5 和客户端比对。
  • 坑4:秒传只比文件名:文件名能改,内容没变就白传。必须用文件内容指纹(MD5 + 大小)判断。
  • 坑5:并发分片不限制:40 片同时发,服务端瞬间被打满,单片失败率反而更高。控制并发数(3~5 片并发),比全量并发更稳。

六、上线后的情况

改造覆盖全部报名表单:弱网下 200MB 视频的传输成功率从约 72% 提到 99.2%;上传中断的学员里,七成会主动点"继续上传"而不是放弃;秒传让重复提交报名表的学员直接把之前的资料带过来,客服"你重新传一下"这句话基本消失了。招生季一个月,表单提交完成率比上期提升了约 18 个百分点。

前端还加了真实的进度条:按"已传分片数 ÷ 总分片数"算百分比,而不是文件大小——弱网下上传进度是稳步往前的,学员知道"还在传、没死掉",放弃率因此降了一截。

印象最深的是一个火车上报名的高中老师:信号断断续续,视频传到 60% 断了两回,每次重连都接着传,最后到站前传完了,还发来一句"这个报名系统终于不折腾人了"。工具好不好用,弱网环境最能说明问题。

结语

大文件上传的本质是"传输不可靠,那就让它可恢复"。分片拆小风险单元、断点续传接住中断、指纹秒传省掉重复、合并校验拦住坏文件——四件事各管一段,弱网不再是"传不上去",而是"传得慢但一定能传完"。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

目录
  • 导读
  • 一、先说背景:为什么资料越传越烦,越烦越传不上去
  • 二、分片:把一个大文件拆成能独立传输的小块
  • 三、断点续传:传了一半断网,接着传而不是重来
  • 四、秒传与合并校验:别让用户重复传同一份资料
  • 五、踩坑清单
  • 六、上线后的情况
  • 结语
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档