Go标准库archive/zip通过FileHeader.UncompressedSize64和CompressedSize64字段可准确获取原始与压缩大小,二者相除得单个文件压缩比;计算整体压缩比需遍历所有非目录文件累加两个计数器,且必须优先使用64位字段以兼容ZIP64格式。

用 archive/zip 读取文件头获取原始与压缩大小
Go 标准库不直接提供“压缩比例”计算函数,但每个 *zip.File 对象都包含 FileHeader.UncompressedSize64 和 FileHeader.CompressedSize64 字段,二者相除即可得单个文件的压缩比。注意:ZIP 文件可能使用数据描述符(data descriptor)或 ZIP64 扩展,此时需确保启用了 zip.OpenReader 而非仅 zip.ReadCloser,否则某些字段可能为 0。
-
UncompressedSize64是解压后字节数,通常可靠;CompressedSize64是 ZIP 流中实际占用字节数,含压缩数据+额外头部(如 DEFLATE 的 zlib 头),但不含 ZIP 中央目录等元数据 - 若文件未压缩(
Method == zip.Store),两者应相等,比例为 1.0 - 遇到
CompressedSize64 == 0且Method != zip.Store,大概率是 ZIP 包损坏或使用了流式写入(无中央目录),此时无法准确计算
遍历 ZIP 内所有文件并累加总压缩比
整体压缩比不是各文件比例的平均值,而是「所有原始字节总和」除以「所有压缩字节总和」。必须遍历全部 *zip.File,跳过目录项(FileInfo().IsDir() == true),并对每个有效文件累加两个计数器。
- 不要用
os.Stat查原始大小——ZIP 内文件可能已被修改,必须依赖FileHeader.UncompressedSize64 - 如果 ZIP 含加密文件(
IsEncrypted()返回true),CompressedSize64仍有效,但无法验证解压后内容,比例仅反映存储层压缩效果 - 示例关键逻辑:
var totalOrig, totalComp uint64 for _, f := range r.File { if f.FileInfo().IsDir() { continue } totalOrig += f.UncompressedSize64 totalComp += f.CompressedSize64 } if totalComp > 0 { ratio := float64(totalOrig) / float64(totalComp) }
警惕 ZIP64 和 32 位溢出导致的数值截断
当单个文件 > 4GB 或 ZIP 总条目数 > 65535 时,ZIP 必须启用 ZIP64 扩展,此时 UncompressedSize64 和 CompressedSize64 才是真实值;而旧字段 UncompressedSize/CompressedSize(uint32)会是 0xffffffff,直接读会导致计算错误。
- 始终优先使用
UncompressedSize64和CompressedSize64,它们在非 ZIP64 文件中也兼容(Go 的zip包会自动填充) - 不要做
if h.UncompressedSize != 0这类判断——它在 ZIP64 下恒为 0,误判会导致跳过有效文件 - 用
go version go1.16+,低版本对 ZIP64 支持不完整,可能 panic 或返回零值
gzip/bzip2/tar.gz 等格式不适用此方法
archive/zip 只处理 ZIP 格式。如果是 .tar.gz,需先用 archive/tar 解包 tar 流,再用 compress/gzip 获取原始大小——但 gzip 本身不存储原始大小(除非尾部有 32 位长度,且未被截断),所以无法精确算比例;.7z、.rar 等需第三方库如 github.com/ulikunitz/xz 或调用外部命令。
立即学习“go语言免费学习笔记(深入)”;
- 单一 gzip 文件(无 tar 封装):可用
gzip.Reader.Header.Comment或解析尾部 8 字节,但不可靠;更稳妥的是解压后os.Stat比较 - 想统一处理多种格式?别硬套 ZIP 方法——每种归档格式元数据结构差异很大,强行复用逻辑只会引入隐蔽 bug
FileHeader 是可信的,其他格式要么没存、要么存得不全。别省那几行代码去绕过标准字段校验,尤其当用户传来的 ZIP 是用 WinRAR 非默认选项生成的。


















