Beego 框架不内置 Gzip 中间件,需手动实现 http.ResponseWriter 包装器:提前设 Content-Encoding、删 Content-Length、拦截 WriteHeader、Write 转发至 gzip.Writer、务必调用 Close() 并检查 error、流式分块压缩、校验 Content-Type 白名单、依赖 Accept-Encoding 头。

Beego 框架本身不内置 Gzip 中间件,直接调用 gzip.Writer 写响应极易出错——不是压缩失败,而是响应头缺失、缓冲未刷、Close() 被忽略,导致客户端收到截断或乱码数据。
Beego 没有 gin.Gzip(),得自己写压缩 wrapper
Beego 的 Controller.ServeJSON() 或 c.Ctx.ResponseWriter 是标准 http.ResponseWriter,但框架没提供开箱即用的压缩封装。你不能像 Gin 那样 router.Use(gin.Gzip()) 一行启用,必须手动构造一个带压缩能力的 ResponseWriter 包装器。
- 核心是实现
http.ResponseWriter接口,内部持有一个gzip.Writer - 必须在第一次写入前设置
Content-Encoding: gzip头,且要删掉可能已设的Content-Length - 所有
Write()调用必须转发给gzip.Writer,不能绕过 -
WriteHeader()要提前拦截:若状态码非 2xx,直接透传原 writer,避免压缩错误响应
手动包装 ResponseWriter 时必须显式调用 Close()
这是 Beego 场景下最常踩的坑:gzip.Writer.Close() 不是可选操作,而是强制要求。它负责写入 GZIP 尾部的 CRC 和 ISIZE 字段,缺了就解压失败——gunzip -t 报 unexpected end of file,Go 解压时抛 gzip: invalid checksum。
- 别依赖
defer w.Close()放在 handler 末尾:万一中间 panic 或提前 return,Close()就漏了 - 正确做法是在所有
Write()完成后立刻调用w.Close(),并检查返回 error(比如磁盘满、网络中断) - 如果 handler 用
c.Data["json"] = ...; c.ServeJSON(),就得在调用前替换c.Ctx.ResponseWriter,并在ServeJSON()返回后立即Close()
大文本必须流式分块,别把整个字符串塞进 bytes.Buffer
Beego handler 里若用 bytes.Buffer 接收原始大文本再压缩,几百 MB 就会 OOM。GZIP 压缩本身不耗内存,但 bytes.Buffer 无节制扩容会吃光 heap。
- 正确路径是“边读边压”:对大字符串按 1MB 切片,每块调用
gzipWriter.Write(chunk)+gzipWriter.Flush() -
Flush()很关键——默认gzip.Writer缓冲 4KB,不 flush 就卡住,客户端收不到首字节 - 别复用底层切片:从
strings.Reader或bufio.Reader读出来的[]byte是共享底层数组的,必须copy()出独立副本再压 - 压缩等级建议用
gzip.BestSpeed(值为 1),大文本传输对延迟敏感,别用BestCompression
Content-Type 不匹配会导致压缩被跳过
Beego 默认不会压缩 text/plain、text/csv 或自定义 MIME 类型,哪怕你手动包装了 writer,如果 Content-Type 不在白名单里,前端可能根本没发 Accept-Encoding: gzip,或者你的 wrapper 逻辑没覆盖到该类型。
- 务必在压缩 wrapper 中检查
c.Ctx.Output.Header().Get("Content-Type") - 只对明确支持压缩的类型启用:比如
strings.HasPrefix(ct, "text/")、ct == "application/json"、ct == "text/csv" - 绝对不要无差别匹配
application/octet-stream:图片、PDF 等二进制文件 gzip 后通常更大,反而拖慢传输 - 客户端必须带
Accept-Encoding: gzip,否则你的压缩逻辑应退化为直传
Beego 的 Gzip 压缩成败,不在压缩算法本身,而在 HTTP 生命周期的精确控制:头要早设、缓冲要勤刷、Close() 要稳落、类型要细筛。漏掉任一环,客户端看到的都不是压缩数据,而是协议层面的残缺流。


















