必须先调用gzip.NewWriter()初始化,再调用Write();未初始化直接Write会panic;写完必须Close(),否则解压报错;默认压缩级别为6,可选0~9或常量调整。

gzip.Write() 之前必须调用 gzip.NewWriter()
直接对 []byte 调用 gzip.Write() 会 panic:因为 gzip.Writer 不是裸结构体,它需要底层 io.Writer 支持流式写入。你不能跳过初始化直接写。
- 正确做法是先用
gzip.NewWriter()包裹一个可写的缓冲区(比如bytes.Buffer) -
gzip.NewWriter()返回的*gzip.Writer才具备Write()方法 - 别忘了在写完后调用
Close()—— 否则压缩头尾不全,解压时大概率报gzip: invalid header - 如果只是临时压缩一小段数据,用
bytes.Buffer最轻量;若要压缩大文件或网络流,直接传http.ResponseWriter或os.File也行
var buf bytes.Buffer
gw := gzip.NewWriter(&buf)
gw.Write([]byte("hello world"))
gw.Close() // 必须!否则 buf.Bytes() 是无效 gzip 流
compressed := buf.Bytes()
compress/gzip 默认压缩级别是 DefaultCompression
不显式设置压缩级别时,gzip.NewWriter() 用的是 gzip.DefaultCompression(值为 6),不是最高也不是最低。这在多数场景够用,但如果你明确追求速度或体积,就得手动选。
-
gzip.NoCompression(0):几乎不压缩,纯封装,适合实时性极高、CPU 敏感的场景 -
gzip.BestSpeed(1):快但压缩率低;gzip.BestCompression(9):慢但最省空间 - 用
gzip.NewWriterLevel()替代NewWriter(),第二个参数填级别 - 注意:级别 >
9或 0 会 panic,不是静默忽略
gw, err := gzip.NewWriterLevel(&buf, gzip.BestSpeed)
if err != nil {
log.Fatal(err) // 比如传了 -1 就会进这里
}
解压时遇到 io.ErrUnexpectedEOF 或 invalid header
这类错误八成不是算法问题,而是压缩端没 Close(),或者把未完成的中间 buffer 当成完整 gzip 数据传给了解压端。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
- 检查压缩侧是否调用了
Close()—— 它会 flush 剩余压缩块并写 footer - 如果压缩后做了截断(比如取前 N 字节)、拼接、base64 编码后没完整还原,也会破坏格式
- 解压时用
gzip.NewReader(),它返回的*gzip.Reader必须被io.Copy()或ReadAll()消费完,否则可能漏读 - 别用
bytes.NewReader(compressed).Read(...)直接读 —— 这绕过了 gzip header 校验,容易静默失败
HTTP 响应中启用 gzip 要手动设 Header 和 Writer
net/http 不自动 gzip 响应体,你得自己包一层 gzip.Writer 并告诉客户端“我发的是 gzip”。
立即学习“go语言免费学习笔记(深入)”;
- 先写
Content-Encoding: gzipHeader,再写状态码和 body - 用
gzip.NewWriter(w)包装http.ResponseWriter,然后往它里面写原始内容 - 务必调用
gw.Close(),否则响应卡住或不完整 - 注意:如果 handler 已经写了 header(比如调了
w.WriteHeader()),再改Content-Encoding就无效了
w.Header().Set("Content-Encoding", "gzip")
gw := gzip.NewWriter(w)
gw.Write([]byte("response body"))
gw.Close() // 这里才真正触发写出
压缩逻辑本身不难,难在边界上:没 Close()、Header 和 Writer 不配套、级别传错、解压时误截断 —— 这些地方一松懈,gzip 就变成“看着像压缩、实际打不开”的黑盒。

















