buffalo控制器中不能直接用c.response().write()返回二进制流,必须用c.blob()或手动设置content-type、content-length后io.copy();c.json()和c.render()会触发序列化或panic,不支持原始流。

Buffalo 控制器里不能直接用 c.Response().Write() 返回二进制流,必须用 c.Blob() 或手动设置 Content-Type + Content-Length + io.Copy()
为什么 c.JSON() 和 c.Render() 不行
这两个方法默认设置 Content-Type: application/json 或模板对应类型,并对输出做序列化或渲染封装。传入 []byte 会触发 JSON 编码(变成带引号的 base64 字符串),传入 *os.File 或 io.Reader 则直接 panic —— Buffalo 的 Render 不处理原始流。
c.Blob() 是最简方式,但注意参数顺序和 MIME 类型
c.Blob() 内部调用 http.ServeContent,适合返回已加载到内存的二进制数据(如图片、PDF、ZIP)。它接受三个参数:status code、MIME type、data []byte。
- 必须显式指定 MIME,比如
"image/png"、"application/pdf",不能依赖文件后缀自动推断 - 如果数据来自磁盘大文件(>10MB),全部读进内存会吃爆 RSS,此时应避免用
c.Blob() - 示例:
c.Blob(200, "video/mp4", data)——data是[]byte,不是string
大文件或流式响应要用 io.Copy() + 手动头设置
当你要返回一个 *os.File、bytes.Reader 或第三方 API 的 io.ReadCloser(如 Livepeer 转码结果流)时,必须绕过 Buffalo 封装:
Buffalo框架 1.0.1 版本源码包下载,适合需要错误处理改进、依赖更新、render.Download 注释和 request logger 调整的 v1 项目。
- 先调用
c.Response().Header().Set("Content-Type", "video/mp4") - 再设
c.Response().Header().Set("Content-Disposition", `attachment; filename="output.mp4"`)(如需下载) - 务必在
io.Copy()前写状态码:c.Response().WriteHeader(200) - 最后
io.Copy(c.Response(), reader),并检查err;别忘了reader.Close()(若为io.ReadCloser)
漏掉 WriteHeader() 会导致 HTTP 状态始终是 200,且部分客户端(如 curl -I)看不到正确 header。
常见坑:中间件干扰流式响应
Buffalo 默认启用的 Logger、RequestID 中间件一般不影响,但以下两个会破坏流:
-
cookies.Secure()和csrf.New():它们尝试读取请求 body 并写入 cookie header,可能提前触发 response flush,导致io.Copy()报http: request method or response status code does not allow body -
gzip.Middleware:如果开启,它会缓冲整个响应再压缩——彻底失去流式意义,且大文件下 OOM - 解决方案:在注册这些中间件时加路径过滤,或在 action 开头用
c.Set("skip_gzip", true)(需自定义 gzip 中间件支持该 flag)
真正要流式吐出视频或音频,Buffalo 只是路由和上下文容器,核心逻辑得回归原生 net/http 那套——别指望框架替你管好 chunked transfer encoding。










