分片上传后需手动合并,gin仅接收请求,合并逻辑须自行实现:校验分片完整性、顺序及大小,加锁防并发,用os.openfile+io.copy流式拼接避免oom。

分片上传后怎么合并成完整文件
合并不是自动发生的,必须由你显式调用逻辑把所有已接收的分片按序读取、拼接写入目标文件。Gin 不提供任何合并能力,它只负责接收 HTTP 请求——分片存储、索引管理、顺序校验、完整性检查全得自己实现。
合并前必须确认哪些状态
直接合并会出错,尤其在断点续传场景下。务必在合并前验证以下几点:
-
identifier对应的所有分片文件是否全部存在(检查chunkNumber是否覆盖1到totalChunks) - 每个分片文件大小是否符合预期(比如最后一片可能小于标准分片大小,但其他片必须等于
ChunkSize) - 可选但强烈建议:对每个分片计算局部 hash,和服务端记录或前端传来的 checksum 比对,防止传输损坏
- 避免并发合并:同一
identifier的合并操作需加锁(如用sync.Map记录进行中任务,或依赖数据库行锁)
用 os.OpenFile + io.Copy 合并比 ioutil.WriteFile 更安全
大文件合并不能一次性读进内存,否则容易 OOM。正确做法是打开目标文件为追加写模式,逐个打开分片、流式拷贝:
dst, err := os.OpenFile(finalPath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0644)
if err != nil {
return err
}
defer dst.Close()
for i := 1; i
<p>注意:<code>os.O_TRUNC</code> 确保从头开始写;<code>io.Copy</code> 是流式、低内存占用;不要用 <code>ioutil.ReadFile</code> + <code>os.WriteFile</code>,那会把整个文件加载进内存。</p>
<h3>合并完成后必须清理临时分片</h3>
<p>合并成功 ≠ 上传完成。不删分片会导致磁盘持续增长、重复合并风险、identifier 冲突等问题。清理动作应放在合并成功后的 <code>defer</code> 或事务末尾,且需容忍失败(比如异步清理或后台定时任务兜底):</p>
- 用
os.RemoveAll(tempDir)删除整个identifier目录最稳妥,比逐个os.Remove分片更可靠 - 若使用数据库记录分片状态,合并成功后要同步更新
IsFinish = true并清空ExaFileChunk关联表 - 别在合并失败时删分片——要保留现场用于重试或排查
最容易被忽略的是:合并逻辑里没处理“部分分片缺失但 totalChunks 已到”的边界情况,比如前端误报 totalChunks=10,实际只传了 9 片,此时强行合并会漏数据且无提示。必须校验数量、大小、顺序三者一致才执行写入。











