zip体积没变化是因为zip.writer默认使用zip.store(无压缩),需显式设header.method = zip.deflate;这是设计使然,并非bug。

zip.Writer 默认不压缩,必须为每个文件显式设置 header.Method = zip.Deflate,否则生成的 ZIP 只是归档,体积不会变小。
为什么写入 ZIP 后体积没变化?
这不是 bug,是 Go 标准库的设计选择:zip.Writer 职责是归档,压缩是可选动作。默认所有条目用 zip.Store(即原样存储),连 zlib 都不调用。
-
zip.Deflate是唯一受支持的压缩方法;zip.BestCompression等常量根本不存在 - 空文件或极小文件(如 Deflate,底层 zlib 也可能自动回退到
Store - 已压缩格式(
JPEG、PNG、MP4)再压基本无效,还可能略增体积;文本类(JSON、.go源码)才明显受益 - 漏掉
zw.Close()会导致 ZIP 文件非法——中央目录区没写入,很多解压工具会静默失败
如何安全解压并防止路径穿越?
直接拼接 f.Name 到目标路径就是 Zip Slip 漏洞,../../../etc/passwd 会被原样创建。
- 必须对每个条目先调用
filepath.Clean(f.Name)归一化路径(./a/../b→b) - 再检查:
cleanPath != f.Name或strings.HasPrefix(cleanPath, "..")或strings.HasPrefix(cleanPath, "/") - 最后确认目标路径在输出目录内:
strings.HasPrefix(filepath.ToSlash(dstPath), filepath.ToSlash(absDest)) - Windows 下还要统一转小写、把
"\"替换为"/",否则"..\..\etc\passwd"可能绕过校验
中文文件名乱码怎么处理?
不是 Go 解析错了,是 ZIP 包本身用了 GBK 编码(常见于 Windows 资源管理器或旧版 7-Zip)。archive/zip 始终按 UTF-8 解析 FileHeader.Name,结果就是一堆 .txt。
- 写入时:Go 1.22+ 可调用
zw.SetUTF8(true),让 ZIP 中的文件名字段标记为 UTF-8 - 读取时:无需额外操作,只要 ZIP 包带 UTF-8 标志位,
f.Name就是正确字符串 - 兼容旧环境(如老版 Windows):无法强制 UTF-8,需接受 GBK 编码,并自行用
golang.org/x/text/encoding/simplifiedchinese.GBK解码f.Name
大文件或大量小文件要注意什么?
误用会让内存暴涨或卡死,但问题不在 archive/zip 本身,而在流式处理逻辑。
- 压缩时避免一次性
io.ReadAll源文件进内存;改用os.Open+io.Copy流式写入 - 解压时别对每个
*zip.File调用file.Open()后再io.ReadAll—— 应该file.Open()后直接io.Copy(dst, src) - 压缩上万个小文件时,注意 OS 文件描述符限制;解压端也要逐个处理,别全 load 进内存
- 超 4GB 或超 65535 个文件时,标准库对 ZIP64 支持有限,校验容易失败,建议换用
github.com/klauspost/compress
Close() 是否遗漏——这些点一旦漏掉,程序在本地跑通,上线后却出 silent failure。golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











