c.FormFile 不能用于分片上传,因其会一次性读取全部分片导致内存溢出;必须禁用自动解析,手动用 multipart.Reader 从原始请求流中按 filename 或 X-Chunk-Index 提取指定分片。

为什么 c.FormFile 不能直接用于分片上传
因为 c.FormFile 会一次性读取整个 multipart 表单字段,包括所有分片数据,导致内存暴涨、超时或 OOM。分片上传必须绕过 Gin 的自动表单解析,直接操作原始请求体流。
- 调用
c.Request.ParseMultipartForm或任何c.PostForm*方法后,c.Request.Body已被消费,后续再读将返回空 -
c.FormFile内部会触发完整解析,无法按需提取单个分片 - 分片上传协议(如 tus、自定义)依赖原始
io.ReadCloser流,必须在解析前接管
如何安全读取原始 multipart 流并提取指定分片
核心是禁用 Gin 自动解析,手动用 multipart.Reader 迭代 part,并根据 filename 或自定义 header(如 X-Chunk-Index)识别目标分片。
- 在路由 handler 开头加
c.Request.ParseMultipartForm(32 是错的——它仍会加载全部到内存;应设为 <code>0或跳过解析 - 正确做法:直接用
multipart.NewReader(c.Request.Body, c.GetHeader("Content-Type")) - 遍历
r.NextPart(),检查part.FormName()是否为"chunk"或匹配你约定的字段名 - 用
part.FileName()获取原始文件名(含分片序号),或从part.Header.Get("X-Chunk-Number")提取元信息 - 务必限制单个分片大小(如
io.LimitReader(part, 10),防止恶意大 part 耗尽内存
分片合并与存储时的路径与权限陷阱
分片落地后不能直接拼接,必须校验顺序、完整性(如 MD5/SHA256)和归属关系,否则可能被伪造或乱序写入。
- 分片临时目录必须隔离,路径应包含用户 ID 和上传会话 ID,例如
./uploads/tmp/{uid}/{upload_id}/chunk_001 - 不要用
os.Create直接覆盖,先写入带随机后缀的临时文件,校验通过后再os.Rename原子提交 - 合并时用
os.OpenFile以os.O_CREATE|os.O_WRONLY|os.O_APPEND模式追加,避免重复写入同一偏移 - 最终文件权限设为
0644,目录设为0755,且确保父目录不可被 Web 服务器直接访问(如不放在Static路由下)
如何让 Gin 支持 tus 协议或兼容标准分片上传客户端
Gin 本身不内置 tus,但可通过中间件 + 手动解析兼容主流前端库(如 Uppy、Dropzone 的分片插件)。
- tus 的
POST /files创建上传,需返回Locationheader 和Upload-Expires;Gin 中用c.Header("Location", "/files/"+uuid) - tus 的
PATCH /files/{id}请求体是纯二进制流,无 multipart 包裹,此时直接io.Copy(dst, c.Request.Body)即可 - 若客户端用传统 multipart 分片(非 tus),需统一约定字段名(如
file)、索引字段(如chunk_index)和总片数(total_chunks),并在 handler 中用c.DefaultPostForm提取元数据,再用原始流读取文件内容 - 所有分片接口必须加 token 校验或 session 绑定,防止未授权上传者伪造分片覆盖他人文件
chunk_index=5 和实际 body 数据必须严格对应,不能靠字段顺序或多次请求间状态推断。建议每个分片请求都携带签名或会话密钥,服务端验证后再写入磁盘。


















