直接存入七牛云应使用 ctx.Request().MultipartReader() 流式上传,避免 ctx.FormFile() 触发临时文件写入或内存溢出;因后者隐含 ParseMultipartForm(32<<20),且 Open() 后传参会导致冗余拷贝。

直接存入七牛云是可行的,但必须绕过本地临时文件写入——echo 默认的 ctx.FormFile() 会把文件读进内存或临时磁盘,这既浪费资源又可能触发超时。真正高效的做法是用 ctx.Request().MultipartReader() 流式解析,再把 io.Reader 直喂给七牛 SDK 的 Put 方法。
为什么不能直接用 ctx.FormFile() 传给七牛
因为 ctx.FormFile() 返回的是 *multipart.FileHeader,它只包含元信息和一个指向临时文件的路径(或内存 buffer),调用 Open() 后才真正加载内容。而七牛的 Put 系列方法支持 io.Reader,但如果你先 Open() 再传,等于多一次拷贝;更糟的是,大文件可能撑爆内存或触发 maxMemory 限制,导致 http: request body too large 错误。
-
ctx.FormFile()隐含了ParseMultipartForm(32 ,默认最多缓存 32MB 到内存 - 七牛
Put接收io.Reader,但不支持“跳过解析、直读原始 multipart 流” - 必须手动调用
ctx.Request().MultipartReader(),拿到*multipart.Reader,再逐 part 解析出文件流
怎么从 ctx.Request() 拿到原始文件流
核心是跳过 ParseMultipartForm,用底层 MultipartReader 手动遍历 parts。关键点:你得自己识别哪个 part 是目标文件(靠 Header.Get("Content-Disposition") 匹配 name="file"),然后把它的 io.Reader 传给七牛。
Echo框架 5.1.0 版本源码包下载,适合关注 RealIP 行为变化、StartConfig.Listener、NewDefaultFS 和观测性中间件入口的开发团队。
- 调用前确保没触发过
ctx.FormValue()或ctx.FormFile(),否则Body已被消费 - 用
mr, err := ctx.Request().MultipartReader()获取 reader - 循环
mr.NextPart(),检查part.Header.Get("Content-Disposition")是否含filename= - 确认后,把
part(它实现了io.Reader)直接传给七牛formUploader.Put()
Put 调用时要注意的三个参数
七牛 formUploader.Put() 的签名是 func(ctx context.Context, ret *storage.PutRet, upToken string, key string, data io.Reader, size int64, extra *storage.PutExtra) error,其中 size 和 extra 容易出错:
-
size必须准确——不能传 -1,也不能靠part.Size(它常为 -1,因 multipart 不强制提供长度);建议提前在前端计算并随表单提交,或用io.Copy(ioutil.Discard, part)先测长(但会消耗流) -
extra至少设storage.PutExtra{MimeType: part.Header.Get("Content-Type")},否则七牛可能识别错类型,导致图片无法预览 -
key建议用随机字符串 + 原文件扩展名,避免冲突;别直接用part.FileName(),它可能带路径或非法字符
完整上传逻辑里最容易漏掉的一步
所有 multipart.Part 的 io.Reader 在被完全读取前,不能结束循环——否则后续 part 会丢失。这意味着:哪怕你只想要第一个文件,也得把整个 multipart 流读完,或者显式调用 io.Copy(io.Discard, part) 清空当前 part。否则下次请求可能卡住,或 ctx.Request().Body 变成 nil。
另外,upToken 必须由服务端生成,且有效期建议控制在 3600 秒内;token 过期会导致 701: invalid token 错误,但错误信息不直观,容易误判为网络问题。

















