分片上传需用唯一uploadId标识同一文件,服务端流式读取并校验分片信息,保存至uploadId目录下防重传。

分片上传时如何识别同一文件的多个切片
关键在客户端上传每个分片时,必须携带能唯一标识文件的字段,比如 fileId(如 UUID)或 fileName + fileSize + fileHash 组合。Gin 服务端不能只靠文件名判断——相同名字的文件可能并发上传,导致分片错混。
推荐做法是:前端生成一个 uploadId(例如用 uuid.NewString()),所有该文件的分片都带这个参数;服务端用它作为临时存储目录名或 Redis key 前缀。别用 md5(fileBytes) 做标识——分片还没拼完,无法计算完整哈希。
- 请求参数中必须包含
uploadId、chunkIndex、totalChunks - 避免用
multipart.FormValue("filename")当唯一标识——浏览器可能自动改名,且不可靠 - 如果用 Redis 记录上传状态,key 建议设为
upload:${uploadId}:status
Gin 中如何安全接收并保存单个分片文件
别直接用 c.FormFile("file") 然后 file.Save() ——这会把整个 multipart body 加载进内存,大分片(如 10MB)容易 OOM。要用 c.MultipartForm() 获取表单,再从 form.File 中取出文件头,用 file.Open() 流式读取写入磁盘。
同时注意校验:chunkIndex 是否越界、uploadId 是否合法、是否已存在同 index 分片(防重传)。
立即学习“go语言免费学习笔记(深入)”;
- 保存路径建议为
./uploads/${uploadId}/${chunkIndex},避免文件名污染 - 写入前先检查目标分片是否已存在,存在则跳过(幂等)
- 务必设置
c.Request.ParseMultipartForm(32 (32MB),否则默认 32KB 会触发 <code>http: multipart: message too large
// 示例:流式保存分片
file, header, err := form.File["file"][0]
if err != nil { return }
src, err := file.Open()
if err != nil { return }
defer src.Close()
<p>dst, err := os.Create(fmt.Sprintf("./uploads/%s/%d", uploadId, chunkIndex))
if err != nil { return }
defer dst.Close()</p><p>io.Copy(dst, src)
合并分片时如何避免竞态与损坏
当所有分片上传完成(chunkIndex == totalChunks - 1),触发合并逻辑。但多个请求可能同时检测到“最后一片”,导致重复合并或文件损坏。不能靠简单文件数量判断——分片可能延迟到达,或网络重传造成重复。
必须引入原子性控制:用 Redis 的 SETNX 或数据库行锁标记“正在合并”,或者更稳妥地——只在收到最后一个分片且确认所有索引 [0, totalChunks-1] 都存在时,才执行合并,并立即 rename 成最终文件。
- 合并前遍历检查
./uploads/${uploadId}/下是否已有0到totalChunks-1全部文件 - 用
os.Rename()替代os.Copy()拼接,减少中间 I/O;拼接用io.MultiReader+os.Create更可控 - 合并成功后清空临时目录,失败则保留日志和分片,供重试排查
前端传参和 Gin 路由设计的实际约束
Gin 默认不支持 PUT/POST 混合表单+JSON,所以分片上传接口必须统一用 POST + multipart/form-data。别试图在同一个接口里既解析 JSON body 又解析文件——Gin 的 c.ShouldBindJSON() 和 c.FormFile() 冲突,会报 http: invalid Read on closed Body。
路由建议拆成两个:一个专收分片(POST /upload/chunk),一个查状态或触发合并(POST /upload/merge 或由最后分片自动触发)。别把 uploadId 放 path 里(如 /upload/:uploadId/chunk)——某些 CDN 或代理会截断长 UUID。
- 所有参数走 form 表单字段,包括
uploadId、chunkIndex、totalChunks、identifier - 响应体返回
{"success": true, "chunkIndex": 5}即可,别返回大文件内容 - Nginx 需调大
client_max_body_size和proxy_buffering off,否则分片可能被截断
分片上传真正的难点不在接收,而在状态一致性——uploadId 生命周期、分片幂等、合并时机、失败回滚,这些环节任何一个没对齐,就会出现“上传完成但文件打不开”这种静默错误。


















