压缩率需手动计算:1 − 压缩后大小 / 原始未压缩总大小;原始大小应优先用 zip.File.UncompressedSize64,为0时流式读取校验;推荐绕过ZIP元数据,用filepath.WalkDir统计源文件总字节数以提升准确性。

用 archive/zip 读取压缩包并计算原始大小
Go 标准库不直接提供“压缩率”API,得自己算:压缩率 = 1 − (压缩后大小 / 原始未压缩总大小)。关键在于准确拿到原始文件总尺寸——不能只看 zip.File.UncompressedSize64,它在某些 ZIP(如含数据描述符、ZIP64 扩展)下可能为 0 或不准确。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
- 遍历
zip.ReadCloser.File列表,对每个zip.File调用File.UncompressedSize64;若为 0,尝试打开该文件并调用io.Copy(ioutil.Discard, reader)流式读取计数(注意:会触发解压,有性能开销) - 务必用
defer zr.Close(),否则文件句柄泄漏,多次检测时会报too many open files - 遇到加密 ZIP 时,
zip.OpenReader不报错但后续Open()会返回zip.ErrFormat或crypto/aes: invalid key,需提前捕获
用 os.Stat 和 filepath.WalkDir 获取原始文件总大小
如果目标是“对比压缩前后体积变化”,且你手上有原始目录(而非仅 ZIP),更可靠的做法是绕过 ZIP 元数据,直接统计源文件总字节数。这能规避 ZIP 元数据不准、重复文件、符号链接等问题。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
- 用
filepath.WalkDir(Go 1.16+)替代旧版filepath.Walk,避免 symlink 循环问题 - 对每个
fs.DirEntry调用entry.Type().IsRegular()过滤掉目录、设备文件等,再用entry.Info().Size()累加 - 注意:Windows 下 NTFS 硬链接会导致重复计数,Linux 下需检查
os.FileInfo.Sys().(*syscall.Stat_t).Nlink避免重复
计算压缩率时的精度与边界情况
压缩率数值本身容易误导——比如空文件、单字节文件、加密 ZIP、含冗余元数据的 ZIP(如注释、额外字段)都会让分母或分子失真。
常见错误现象:
-
compress ratio = NaN:原始大小为 0(如 ZIP 里全是空目录),需提前判断并跳过或标记为 “N/A” - 结果 > 100%:说明压缩后比原始还大,常见于已压缩文件(JPEG/PNG)、小文本、ZIP 中含大量元数据;这不是 bug,是真实现象
- 结果为负数:通常是
UncompressedSize64溢出(ZIP64 未正确解析),应降级用流式读取校验
建议统一用 float64 计算,并保留三位小数:math.Round(1e3*(1-float64(zipped)/float64(original)))/1e3
完整流程中易被忽略的 I/O 错误处理
实际脚本跑在线上或 CI 里,常因权限、路径不存在、磁盘满等失败,但错误被静默吞掉,导致压缩率显示为 0 或 panic。
必须检查的点:
-
os.Stat返回os.IsNotExist(err)或os.IsPermission(err),不能只判err != nil -
zip.OpenReader失败时,可能是损坏 ZIP,也可能是非 ZIP 文件(如传入了 .tar),建议先用mimetype库或魔数检测(前 4 字节是否为0x50 0x4b 0x03 0x04) - 流式读取解压内容时,
io.Copy可能返回io.ErrUnexpectedEOF,代表 ZIP 截断,此时应终止并标记 “corrupted”
压缩率不是黑盒指标,原始数据来源、ZIP 构建方式、Go 版本对 ZIP64 的支持程度,都会影响结果一致性。别只信一个数字。


















