gzip.Writer 必须显式调用 Close 才能写入完整压缩流,否则因缓冲区未刷新且缺少尾部校验信息,解压时会报 unexpected EOF 或 invalid checksum。

gzip.Writer 必须 Close 才能写入完整压缩流
不调用 gzipWriter.Close() 会导致压缩数据不完整,解压时抛出 unexpected EOF 或 invalid checksum。这是因为 gzip.Writer 内部有缓冲区,Close() 不仅刷新缓冲,还写入 gzip 尾部(包括 CRC32 和长度校验),缺一不可。
常见错误是只 defer Close() 却在 io.Copy 后提前返回错误,导致 defer 没执行。正确做法是显式检查并关闭:
- 在
io.Copy后立即检查 error,若出错需手动调用gzipWriter.Close() - 避免在
defer中依赖资源未 panic 的前提——panic 会跳过 defer - 对大文件建议用
io.CopyBuffer(dst, src, make([]byte, 1 控制内存占用
archive/zip 并发写入 ZIP 需要同步控制
zip.Writer 本身不是 goroutine-safe 的,多个 goroutine 直接往同一个 zip.Writer 写会导致文件结构损坏、条目丢失或 zip: not a valid zip file 错误。
有两种安全模式:
立即学习“go语言免费学习笔记(深入)”;
- 单 goroutine 主控:用 channel 收集待压缩文件路径,主 goroutine 逐个调用
writer.CreateHeader()+io.Copy,其他 goroutine 只负责读取和预处理(如计算哈希、过滤) - 分片压缩再合并:每个 goroutine 压缩一个子目录为独立
.zip,最后用archive/zip的底层 API(非zip.Writer)拼接中央目录(复杂,一般不推荐) - 更实用的替代:改用
fastzip库(2026 年已稳定),它原生支持并发添加文件,且默认启用Deflate并行压缩
大文件解压时路径遍历必须做安全校验
直接使用 file.Header.Name 创建文件可能触发路径穿越(path traversal),比如恶意 ZIP 包含 ../../../etc/passwd 条目,解压后覆盖系统关键文件。
标准库不自动过滤,必须手动校验:
- 用
filepath.Clean(file.Header.Name)规范路径 - 检查清理后路径是否仍以目标解压根目录为前缀:
strings.HasPrefix(cleaned, targetRoot+string(filepath.Separator)) - 拒绝任何含
".."或开头为"/"的路径(Windows 下还要检查"\"和盘符) - 对目录条目(
file.FileInfo().IsDir())也需同样校验,否则可能创建越界目录
TB 级压缩应避开 gzip,优先选 zstd 或 lz4
gzip 在 TB 场景下 CPU 利用率高、吞吐低,实测压缩速度常低于 50 MB/s(单核),且无法有效利用多核。而 zstd(通过 github.com/klauspost/compress/zstd)在同等压缩比下快 3–5 倍,支持原生并发压缩帧。
选型建议:
- 冷数据归档:用
zstd.WithEncoderLevel(zstd.EncoderLevelFromZstd(3))(中速+高压缩) - 实时日志管道:用
lz4(github.com/pierrec/lz4/v4),单核轻松破 500 MB/s - 兼容性第一:保留 gzip,但启用多 goroutine 分块压缩(需自行切分文件并合并帧头/尾)
- 注意:标准库
compress/gzip不支持并发,别试图用多个gzip.Writer写同一文件


















