后端如何处理大文件上传与断点续传

域名注册
域名注册 正式会员超兽战士 👑年卡会员
发布于 2026-10-07 18:14 ·1 浏览 ·0 回复

大文件上传的后端方案可以压缩成三件事:分片上传 + 分片状态落库 + 幂等合并。所谓「断点续传」,后端并不需要记住什么进度,它只需要在客户端重新发起时,如实回答「这个文件的第 3、7、9 片我还没收到」,剩下的交给前端跳过即可。

后端处理大文件上传的核心思路是什么?

结论:把 5GB 的文件切成 1000 个 5MB 分片,逐片上传、逐片记录、最后顺序合并,是当前最通用也最容易落地的做法。

前端先算文件指纹(推荐 spark-md5 在 Web Worker 里增量计算,避免卡住主线程;对超大文件可用「抽样哈希」——取前 2MB + 中间 2MB + 末尾 2MB + 文件大小拼起来算 md5,碰撞概率在业务上可接受),然后带着 fileMd5、fileSize、fileName、chunkSize、chunkTotal 调一次 POST /api/upload/init。

这个 init 接口是整个方案的大脑,它要返回三样东西:是否命中秒传(instant=true)、本次上传的 uploadId、以及已经成功接收的分片索引数组(例如 [0,1,2,5,6])。前端拿到数组后,只上传缺失的那几片,这就是断点续传的全部秘密。

分片大小和并发数怎么定?

结论:分片用 5MB,浏览器并发 3~5 个,是延迟、内存和重试成本之间最舒服的取值。

5MB 不是随便定的:S3、OSS、MinIO 的 Multipart Upload 都要求除最后一片外每片不小于 5MB,用这个值可以做到「本地存储」和「对象存储」两套实现无缝切换。

分片太大(比如 100MB),单片失败要重传的代价高,浏览器内存压力也大;分片太小(比如 100KB),5GB 文件会产生 5 万个请求,元数据表和合并阶段的文件句柄都会成为瓶颈。

并发数超过 8 之后,收益基本被磁盘 IO 和带宽吃平,反而更容易触发网关限流。建议在 Gateway / Nginx 侧同步放开 client_max_body_size 20m、proxy_request_buffering off、proxy_read_timeout 300s,否则分片上传会被网关直接截断。

断点续传具体怎么实现?

结论:续传能力来自服务端的一张分片表,而不是来自客户端的记忆。

建两张表就够了:

  • file_upload:upload_id(主键)、file_md5、file_size、chunk_total、status(init/uploading/merging/done/failed)、user_id、expire_at
  • file_chunk:upload_id、chunk_index、chunk_size、chunk_md5、storage_path,联合唯一索引 (upload_id, chunk_index)

分片上传接口 POST /api/upload/chunk 必须做成幂等的:先查 (upload_id, chunk_index) 是否已存在,存在就直接返回成功,不重复落盘。写入时先写临时文件 {index}.part.tmp,校验分片 md5 通过后再 rename 成 {index}.part——rename 在同一文件系统内是原子操作,能避免并发写同一索引导致半个文件。

uploadId 的生成策略有两种:一是每次 init 都新建(前端把 uploadId 存 localStorage 用于续传),二是直接用 fileMd5 作为 uploadId(同一文件天然续传,刷新页面也不丢)。第二种更省事,但要求同一用户不能并发上传同名同内容的文件。

合并怎么做才不爆内存、不重复合并?

结论:合并必须流式顺序追加,并且用分布式锁保证同一 uploadId 只有一个合并任务在跑。

POST /api/upload/complete 进来后,先校验分片数量等于 chunkTotal,缺片则返回 400 并附上缺失索引列表(前端可以继续补传)。然后按 chunk_index 升序,用 8KB~64KB 缓冲区逐个 read + write 追加到最终文件,全程内存占用恒定在几十 KB,千万不要把分片全读进 byte[] 再拼。

合并前用 Redis SETNX lock:upload:{uploadId} 加锁,TTL 设 300 秒,防止用户连点两次「完成」生成两份文件。合并完成后重新计算一次最终文件 md5,与 init 时上报的值比对,一致才把状态改成 done 并写入正式业务表,不一致则标 failed 并清理。

如果后端直接用对象存储,合并这步可以省掉——调 CompleteMultipartUpload 由存储服务端完成,性能远好于应用层拼接。

秒传和临时数据清理要注意什么?

结论:秒传本质是「相同文件只存一份」,但必须做好权限隔离,否则等于给别人送文件。

命中秒传时,不要直接返回已有文件的下载地址,而是为当前用户新建一条文件引用记录(引用已有的物理存储路径),物理文件只存一份。这样用户 A 秒传的文件不会出现在用户 B 的列表里。

另外,未完成的上传会持续占用磁盘。加一个定时任务,每小时扫描 expire_at < now() 且状态不是 done 的 uploadId,删除其分片目录和数据库记录,临时数据保留 24 小时是常见配置。前端侧则配合指数退避重试(1s、2s、4s,最多 3 次)和 visibilitychange 暂停逻辑,让弱网下的续传体验真正可用。

一句话收尾:后端做大文件上传,重点不是「怎么传得快」,而是「怎么让每一次重复请求都安全无副作用」——幂等分片、锁保护合并、定时清理,这三条守住了,断点续传和秒传都是水到渠成的事。

版权声明:本文来自 GJ站长论坛《后端如何处理大文件上传与断点续传》
原文链接:https://www.gj0.com/thread-464.html
转载请注明出处并保留本声明;内容仅代表作者观点,与本站立场无关。若本文涉嫌侵权,请联系本站处理。

全部回复 0

还没有回复,来抢沙发~