应手动提取 chunk、chunks 等字段并校验顺序,用 c.PostForm() 读索引、c.FormFile() 读分片;避免直接 FormFile 读整个文件,需提前调用 ParseMultipartForm 设置内存上限。

分块上传时如何正确解析 multipart/form-data 中的 chunk 数据
Gin 默认能处理 multipart/form-data,但分块上传通常依赖前端按约定传入 chunk、chunks、filename 等字段,Gin 不会自动识别这些语义。必须手动提取并校验,否则容易漏 chunk 或错序。
常见错误是直接用 c.FormFile("file") 试图读整个文件——这在分块场景下会失败,因为单次请求只含一个分片。
- 用
c.Request.MultipartReader()获取原始 reader,再调用mime.NewMultipartReader()手动解析(适合需要精细控制边界的情况) - 更常用的是:先用
c.PostForm("chunk")、c.PostForm("chunks")读索引和总数,再用c.FormFile("file")读当前分片文件体 - 注意:Gin 的
FormFile会自动调用ParseMultipartForm,若未提前设置最大内存(c.Request.ParseMultipartForm(32 ),大分片可能触发 <code>http: request body too large错误
临时分片文件该存哪、怎么命名才安全
分片不能存在内存里(OOM 风险),也不能硬编码路径如 /tmp/xxx——多实例部署或并发上传时易冲突、权限报错、清理困难。
推荐方案是按唯一标识(如 md5(filename + upload_id))生成上传会话 ID,所有分片存到 ./uploads/{session_id}/chunk_{index} 下。
立即学习“go语言免费学习笔记(深入)”;
- 上传 ID 最好由前端生成并随每个分片传递(如
upload_id字段),避免后端生成后需额外接口同步 - 文件名必须过滤:用
path.Clean()处理c.PostForm("filename"),防止../etc/passwd路径遍历 - Linux 下建议用
os.OpenFile(..., os.O_CREATE|os.O_WRONLY|os.O_EXCL)创建分片,利用原子性避免重复写入
合并分片时如何保证顺序和完整性
合并不是简单按文件名排序拼接,因为 chunk_10 会排在 chunk_2 前面;且缺少任一分片会导致最终文件损坏。
必须显式校验分片数量与哈希(如每个分片附带 chunk_hash 字段),再按 chunk 数字索引升序合并。
- 用
strconv.Atoi(c.PostForm("chunk"))解析索引,而非字符串排序 - 合并前检查目录下是否存在从
0到chunks-1的全部分片文件,缺一不可 - 用
io.Copy逐个追加(不要全读进内存),目标文件用os.O_CREATE|os.O_WRONLY|os.O_TRUNC打开 - 合并完成后立即计算最终文件
sha256,与前端传来的total_hash对比,不一致则删掉并返回错误
Gin 路由中如何避免阻塞和超时导致上传中断
默认 Gin 的 HTTP server 没有调大读取超时,大文件分片上传慢时会直接断连,报错 broken pipe 或 connection reset。
这不是 Gin 代码问题,而是底层 http.Server 配置缺失。
- 启动时显式配置:
&http.Server{Addr: ":8080", ReadTimeout: 30 * time.Minute, WriteTimeout: 30 * time.Minute} - 禁止在 handler 里做耗时操作(如合并完再同步计算视频缩略图),应丢给 goroutine 或消息队列
- 合并成功后,用
os.RemoveAll("./uploads/{session_id}")清理分片,但别在响应前阻塞等待——可异步执行
最易被忽略的是:分片上传期间服务重启,临时目录残留大量未合并文件。得配个定时任务或用 filepath.Walk 扫描过期(如 24 小时无更新)的 session 目录清理。


















