有时我们可能会遇到需要把多个 gzip 文件合并成单个 gzip 文件的场景,最简单最容易的方式是把每个gzip
文件都先解压,然后合并成一个文件后再次进行压缩,最终得到我们想要的结果,但这种先解压后压缩的方式显然效率不高,有没有更好的实现方式呢,答案是肯定的,Linux下常用的压缩库 Zlib 的两位主要作者之一 Mark Adler 就给我们提供了一个这样的示例程序:
https://github.com/madler/zlib/blob/develop/examples/gzjoin.c
从说明中我们可以看出,这种方式只需要解压一遍所有文件(用于找到特定的比特位并修改它),但不需要做任何额外的压缩操作,而且合并后的 gzip
文件末尾的 crc32
校验和也不需要从头计算(根据源 gzip 文件的校验和用函数 crc32_combine
便可计算出),一般来说,解压操作比压缩速度更快,所以这种合并 gzip
文件的方式性能相当高效,如果是在 C 语言中实现,我们就可以直接借鉴 Mark Adler 大佬的代码。
Go 语言实现
用 Go 内置的 compress/gzip
或者 compress/flate
包无法实现与 gzjoin.c
相同的功能,因为 gzjoin.c
的实现依赖解压时的 Z_BLOCK
刷写模式,而 compress/flate
解压缩时并不支持指定 Flush 模式,所以我们只能换一种思路,利用 cgo 来直接调用 Zlib
C 库,具体实现可以参考我这里的代码 https://github.com/zhyee/deflatejoin,如果对实现细节不感兴趣,也可以在你的Go项目中直接调用该module,相比用 解压
--> 合并文件
--> 再压缩
的方式速度有极大提升!
goos: darwin
goarch: arm64
pkg: github.com/zhyee/deflatejoin
BenchmarkConcatGzip/concat-standard-go-8 9 123559972 ns/op 1257826 B/op 1261 allocs/op
BenchmarkConcatGzip/concat-deflatejoin-8 100 10784015 ns/op 30289 B/op 41 allocs/op