archive/zip无法直接获取压缩比,因为CompressedSize64在w.Close()前为0,需先写入并关闭ZIP,再通过zip.NewReader解析FileHeader获取真实大小,最后用总原始大小除以ZIP文件总大小(buf.Len())计算压缩比。

为什么 archive/zip 无法直接获取压缩比
Go 标准库的 archive/zip 包在写入 ZIP 文件时默认使用 zip.Deflate(即 DEFLATE 算法),但不暴露压缩前后的原始字节量,也不提供“压缩比”字段。你调用 zip.Writer.Create() 或 zip.FileHeader 时,UncompressedSize64 和 CompressedSize64 在写入完成前是零值;只有 ZIP 文件写完并关闭后,才能从磁盘读取文件头或重新解析 ZIP 结构来提取这两个值。
计算压缩比的可靠步骤
必须分两步:先生成 ZIP 文件(或内存 buffer),再解析其内部 FileHeader 获取真实大小。不能靠估算或临时变量赋值——因为实际压缩结果受内容、块边界、ZIP 元数据填充等影响。
- 用
zip.NewWriter写入所有文件,然后调用w.Close()强制 flush 并写入 central directory - 若写入磁盘:用
os.Stat()获取 ZIP 文件总大小(即压缩后大小),再用zip.OpenReader遍历每个FileHeader的UncompressedSize64累加得原始大小 - 若写入
bytes.Buffer:先w.Close(),再用bytes.NewReader(buf.Bytes())构造 reader,传给zip.NewReader - 注意:单个 ZIP 可含多个文件,压缩比应按「总原始大小 / 总压缩后大小」算,而非平均或单个文件比
zip.FileHeader.CompressedSize64 为 0 的原因和修复
这是最常踩的坑:直接读 FileHeader 字段发现 CompressedSize64 == 0。这是因为标准库在创建 FileHeader 时只设置 UncompressedSize64,而 CompressedSize64 必须由 ZIP writer 在写完该文件数据后回填到 local header 和 central directory 中——这个动作只在 w.Close() 后才完成。
- 错误做法:
h := &zip.FileHeader{...}; h.CompressedSize64直接读 —— 一定为 0 - 正确做法:用
zip.OpenReader或zip.NewReader重新打开已关闭的 ZIP 数据,再遍历Reader.File中的FileHeader - 特别注意:
FileHeader.CompressedSize64是 ZIP 文件中该文件数据块的实际长度(不含 local header、file data descriptor 等元数据),所以它比 ZIP 总大小小;总压缩后大小应取os.Stat().Size()或buf.Len()
一个可运行的最小示例
以下代码将一个字符串写入 ZIP 并计算压缩比(无文件 IO,纯内存):
立即学习“go语言免费学习笔记(深入)”;
buf := new(bytes.Buffer)
w := zip.NewWriter(buf)
f, _ := w.Create("data.txt")
f.Write([]byte("hello world hello world hello world"))
w.Close() // 关键:不调用则 buf 里没有 central directory,无法解析
r, _ := zip.NewReader(bytes.NewReader(buf.Bytes()), int64(buf.Len()))
var totalOrig, totalComp int64
for _, fh := range r.File {
totalOrig += int64(fh.UncompressedSize64)
totalComp += int64(fh.CompressedSize64) // 此时才是真实值
}
ratio := float64(totalOrig) / float64(buf.Len()) // 注意:分母是整个 ZIP 文件大小,不是 totalComp
这里 buf.Len() 是 ZIP 文件总大小(含所有元数据),而 totalComp 是所有文件数据块之和,二者不等——压缩比定义是「原始数据量 / 最终 ZIP 文件体积」,所以分母必须用 buf.Len()。
真正容易被忽略的是:ZIP 文件本身有结构开销,空 ZIP 也有 22 字节;小文件压缩后可能反而更大;DEFLATE 对短重复串效果差——这些都会让压缩比 1.0。别硬套理论值,以实测 buf.Len() 为准。


















