分片上传必须校验upload_id和chunk_number,服务端需三重检查:查redis中是否已存在该分片、检查chunk_number是否越界、拒绝无upload_id或空chunk_number请求;合并前须用redis原子计数确认完整性,并流式合并避免oom。

分片上传接口必须校验 upload_id 和 chunk index
不校验就直接写入,会导致脏数据堆积、合并失败或覆盖。关键点是:每个分片请求必须携带 upload_id(如文件 MD5)和 chunk_number(从 0 或 1 开始的整数),服务端需做三重检查:
- 查 Redis 中该
upload_id对应的已接收分片集合:redis.SMembers("upload:" + uploadID + ":parts"),若已存在该chunk_number,返回409 Conflict - 检查
chunk_number是否越界(比如chunk_number >= total_chunks),越界直接400 Bad Request - 拒绝无
upload_id或空chunk_number的请求,防止误触临时目录
合并前必须用 Redis 原子计数确认完整性
前端传的 total_chunks 不可信——网络丢包、客户端 bug、重复提交都可能导致它与实际不符。真正可靠的判断依据只有一个:
redis.SCard("upload:" + uploadID + ":parts") == total_chunks
这个操作必须在合并前执行,且应包裹在事务或锁中(如 redis.Watch + redis.Multi)。否则可能出现「刚查完数量对得上,下一秒另一个并发请求删掉一个分片」的竞态。
常见错误是只比对前端传的 total_chunks 和本地磁盘文件数,但磁盘可能残留旧分片、或部分写入失败未清理,导致合并出错。
合并时避免一次性读入全部分片到内存
单个分片按 5–10MB 设计,1GB 文件约 100–200 片。若用 os.ReadFile 全部加载再拼接,内存峰值会飙升到数 GB,极易触发 OOM。
正确做法是流式合并:
- 按
chunk_number顺序打开每个分片文件(路径如/tmp/uploads/abc123/0001.bin) - 用
io.Copy将每个分片内容逐个写入目标文件句柄,不缓存全文本 - 合并完成后立即
os.RemoveAll分片目录,防止磁盘爆满
示例关键片段:
f, _ := os.OpenFile(finalPath, os.O_CREATE|os.O_WRONLY, 0644) defer f.Close() for i := 0; i <h3>Nginx 层必须调大缓冲和超时参数</h3> <p>Gin 处理逻辑再严谨,如果 Nginx 在中间把分片截断或提前关闭连接,一切白搭。重点配以下三项:</p>
-
client_max_body_size至少设为单片最大值(如10M),不能只设成总文件大小 -
client_body_buffer_size建议128k,避免小分片反复落盘到client_body_temp_path -
client_body_timeout和proxy_read_timeout都要设为 ≥300s,覆盖慢网下上传单片耗时
漏配 client_body_timeout 是最隐蔽的坑——Nginx 默认 60s,用户上传第 50 片时卡顿 61 秒,连接就被静默断开,Gin 根本收不到请求。











