必须调用gz.close()才能获得真实压缩数据,否则buf.len()返回原始长度或0;因gzip.writer仅在close()时写入crc/isize尾部,flush()不写尾部,解压会失败。

必须显式调用 gz.Close() 才能拿到真实压缩后大小;只写不关,buf.Len() 返回的往往是原始长度或 0。
为什么 buf.Len() 常常不准
gzip.Writer 是缓冲写入器,Write() 只把数据送进内部压缩 buffer,并不立即生成压缩字节。直接读 buf.Len() 会拿到未压缩的原始输入(如果还没 flush)或空结果(如果只写了但没 close)。常见错误现象包括:
- 压缩后字节数和原文一样大
-
buf.Bytes()是空切片或长度异常小 - 解压时报
gzip: invalid header或unexpected EOF
根本原因:gzip 格式要求尾部有 8 字节 trailer(CRC32 + ISIZE),只有 Close() 才写入;Flush() 只输出压缩块,不写 trailer。
measureCompression 函数必须包含 Close() 和 Flush() 吗
必须调 Close(),Flush() 在多数场景下非必需,但加了更健壮。关键点:
-
Close()是强制项:它触发最终压缩、写入 trailer、释放资源 -
Flush()可选但推荐:确保所有已写数据进入压缩流程,避免因 panic 或提前 return 导致部分数据滞留 - 不能用
defer gz.Close()放在函数开头——若Write()报错提前返回,defer还没执行,压缩就丢了
正确顺序示例:
gz := gzip.NewWriter(&buf)
start := time.Now()
_, err := gz.Write(inputBytes)
if err != nil {
return
}
err = gz.Flush() // 确保压缩启动
if err != nil {
return
}
err = gz.Close() // 必须!写入 trailer 并完成
if err != nil {
return
}
compressed := buf.Bytes()
fmt.Printf("Compressed size (bytes): %d\n", len(compressed))
如何安全获取原始字符串字节数
Go 字符串底层是 UTF-8 编码的只读字节序列,len(s) 直接返回字节数,不是 rune 数。这点极易混淆:
-
len("你好") == 6(每个汉字占 3 字节),不是 2 - 别用
utf8.RuneCountInString(),那是 rune 个数,和压缩无关 - 压缩前必须转成
[]byte再取len(),因为gzip.Writer.Write()接收的是[]byte
所以测量起点就是:
inputBytes := []byte(input) originalSize := len(inputBytes) // 这才是压缩前真实字节数
解压后怎么验证字节数一致
解压后也必须用 len() 检查还原结果,且要确保解压完整:
- 用
gzip.NewReader(bytes.NewReader(compressed))包装压缩数据 - 用
io.ReadAll()或io.Copy(&dst, gr)读满整个流,不能只Read()一次 - 解压失败时
io.ReadAll()会返回 error,此时len(dst.Bytes())不可信 - 对比
len(originalBytes)和len(decompressed),二者必须相等才算无损
最容易被忽略的一点:HTTP body、文件读取、网络流等来源的数据,必须确认已读满整个压缩体——少一个字节,gzip.NewReader 就可能报 unexpected EOF,导致解压中断,字节数自然对不上。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











