gzip压缩在go中需手动处理请求、响应及缓存三类场景;客户端须用gzip.newwriter压缩数据并设正确content-length,close()不可省略;服务端响应需检查accept-encoding并动态设置content-encoding: gzip,或使用gorilla/handlers.compresshandler。

Gzip 压缩在 Go 里不是“开个开关”就完事的,它分客户端发请求、服务端回响应、以及中间缓存处理三类场景——每类都要手动干预,标准库不自动帮你压或解(除了响应自动解压这个例外)。
为什么 http.Client 发请求时加了 Content-Encoding: gzip 却报 gzip: invalid header
因为你只加了头,没真正压缩数据。Go 的 http.Client 不会读你写的头然后自动压缩 Body;它只原样转发你塞进去的字节流。
- 必须先用
gzip.NewWriter把原始 payload 压缩成[]byte,再传给bytes.NewReader -
Content-Length必须设为压缩后长度,不能沿用原始数据长度 -
gzip.Writer.Close()不可省略——漏掉会导致尾部 CRC32 和 ISIZE 缺失,解压端直接失败 - 别用
buf.Bytes()在Close()前调用,此时 buffer 还没 flush 完整 gzip 流
示例关键片段:
gz := gzip.NewWriter(&buf)
gz.Write(payload)
gz.Close() // ← 这步之后才能 buf.Bytes()
req, _ := http.NewRequest("POST", url, bytes.NewReader(buf.Bytes()))
req.Header.Set("Content-Encoding", "gzip")
req.Header.Set("Content-Length", strconv.Itoa(buf.Len()))
怎么让 http.ResponseWriter 返回 Gzip 响应
标准库没有内置压缩中间件,要么自己包一层 ResponseWriter,要么用 github.com/gorilla/handlers.CompressHandler 这种成熟封装。
- 手动实现时,得检查
r.Header.Get("Accept-Encoding")是否含gzip - 一旦决定压缩,就要在首次
Write前设置Content-Encoding: gzip,并删掉可能已存在的Content-Length(压缩后长度不可预知) - 不能对状态码是
204、304或 header 已写过的响应再套 gzip writer - 若用
CompressHandler,它会自动跳过小响应(默认
为什么 compress/gzip 压缩后解不开,或者报 unexpected EOF
90% 是因为忘了调 Close();剩下 10% 是误把 deflate 数据当 gzip 传给了 gzip.NewReader。
-
gzip.Writer.Close()才会写入尾部元数据(CRC32 + ISIZE),Flush()不行 -
gzip.NewReader只认完整 gzip 格式(magic bytes0x1f 0x8b开头),不接受裸 deflate - 如果服务端用的是
Content-Encoding: deflate,得换用flate.NewReader或zlib.NewReader - HTTP chunked 编码的数据流,要先剥离 chunk 头再喂给
gzip.NewReader,否则头不对
最常被忽略的一点:压缩级别不是越高越好。gzip.BestCompression 对短文本(比如 JSON API 响应)几乎不提升体积,但 CPU 和延迟翻倍;日常用 gzip.DefaultCompression(=6)足够,实时场景可降为 gzip.BestSpeed(=1)。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











