Revel接收前端Blob需从Controller.Params.Files手动获取multipart.FileHeader并调用Open()读取,避免io.ReadAll导致OOM;输出用RenderBinary设正确Header,注意调整MaxMultipartMemory防400错误。

Revel 中接收前端 Blob 数据要靠 Controller.Params.Files
Revel 默认不自动解析 multipart/form-data 里的二进制文件,它把原始文件数据交给你自己处理。你得从 Controller.Params.Files 里手动取,而不是依赖 Params.Get 或 Params.JSON。
常见错误是直接用 c.Params.Get("file") 拿 Blob 字段——这只会返回空字符串或字段名,因为 Blob 实际上传为 multipart.FileHeader 类型,存在 Files 映射中。
-
c.Params.Files是map[string][]*multipart.FileHeader,键是表单字段名(如"avatar"),值是同名文件列表 - 每个
*multipart.FileHeader包含Filename、Size、Header,但**不含内容本身**;需调用Open()才能拿到multipart.File(即io.ReadCloser) - 务必在读取后
defer file.Close(),否则句柄泄漏
读取 Blob 内容时别用 io.ReadAll 直接加载大文件
前端传来的 Blob 可能是几 MB 的图片或 PDF,io.ReadAll 会一次性把全部内容读进内存,容易触发 OOM 或拖慢整个请求处理流程。
更稳妥的做法是边读边处理,比如流式写入磁盘、转发给对象存储、或校验哈希:
- 用
io.Copy(dst, src)把multipart.File直接复制到*os.File或bytes.Buffer(小文件才用后者) - 若需校验,用
hash.Hash包套一层io.TeeReader,避免二次读取 - 不要用
file.Header.Size做唯一信任依据——攻击者可伪造,应以实际读取字节数为准
Revel 向前端输出二进制流要用 RenderBinary 而非 RenderText
想让浏览器下载文件或渲染图片,必须设置正确的 Content-Type 和 Content-Disposition,且响应体不能经过模板渲染。Revel 提供了 c.RenderBinary(data []byte, filename string) 封装,它会自动:
- 设置
Content-Type: application/octet-stream(除非你显式覆盖) - 添加
Content-Disposition: attachment; filename="xxx"(当filename != "") - 跳过所有模板中间件,直接写入 HTTP body
如果你自己用 c.Response.Out.Write(),漏掉 Content-Type 或没设 Content-Length,前端可能收到乱码或被当成文本解析。另外注意:RenderBinary 不接受 *os.File 或 io.Reader,只收 []byte ——大文件需先读入内存或改用 c.Response.Out 手动流式写入并自行设 header。
前端 Blob 与 Revel 后端类型不匹配的典型报错
最常遇到的是 multipart: NextPart: bufio: buffer full 或 http: request body too large,根源往往不在 Revel 本身,而在 Go HTTP Server 的默认限制:
- Revel 底层用
net/http.Server,其MaxMultipartMemory默认仅 32MB;上传超限会直接 400,且不会进 Controller - 解决方法:在
app/init.go的init()函数里修改revel.ServerConfig.MaxMultipartMemory = 100 (即 100MB) - 若用 Nginx 做反代,还必须同步调大
client_max_body_size,否则请求根本到不了 Revel - 前端用
fetch传 Blob 时,body必须是FormData实例,不能是ArrayBuffer或Blob直传——否则后端收不到multipart结构
真正麻烦的不是怎么传,而是哪一层在截断:浏览器、反代、Go HTTP Server、Revel 参数解析——得逐层确认日志和错误位置,不然容易在错误的地方加调试。











