7AA7 博客

压缩包格式怎么选:zip、7z、tar 的适用场景与坑

朔风 UID:67
2026-10-03 16:50
38 4 2

打包文件这件事,日常天天做,但真要问”该用哪个格式”,很多人是凭习惯:默认双击用哪个就一直用哪个。实际上 zip、7z、tar.gz 这几类格式的定位差别不小,选错的代价从”对方打不开”到”安全漏洞”都有。

一、zip:兼容性的默认答案

zip 的最大优势是不用解释。Windows 从 XP 时代就原生支持右键解压,macOS 的归档工具认它,各种网盘、邮件附件、在线预览大多对它最友好。给别人发文件、往各种奇奇怪怪的系统里传资料,zip 是犯错概率最低的选择。

zip 格式本身是上世纪 80 年代末定型的,结构简单:一串本地文件头 + 中央目录。这个结构带来两个特点——解压实现极其简单(到处都能解),以及压缩率和功能上限有限。默认的 deflate 算法在今天看来中规中矩,文件数量多、小文件多的场景下,额外的目录条目开销会更明显。

实操上有几个值得注意的点:

编码问题。 zip 规范本身不定义文件名编码,Windows 自带压缩写的是 GBK(系统代码页),Linux 的 unzip 默认按 UTF-8 或 CP437 猜。中文文件名的 zip 在跨系统传输后变乱码,根源就在这里。带 BOM 的 UTF-8 文件名标志位(EFS flag)是后来补的约定,但不是所有工具都认。给别人发含中文名的压缩包,最稳的办法是把文件名先转成英文,或者用支持 UTF-8 标志位的工具打包,并和对方确认解压工具。

路径分隔符。 zip 规范用正斜杠,但 Windows 的打包工具有时写反斜杠,某些严格实现的解压器会把整个路径当成一个文件名。

加密强度。 zip 传统的 ZipCrypto 加密是几十年前的设计,今天已知存在明文攻击的口子,只适合”防君子”的场景。WinZip AES 模式(AE-2)强度上来很多,但不是所有解压工具都支持。需要真加密时,zip 不是首选。

二、7z:压缩率优先

7z 是 7-Zip 项目推出的格式,主打高压缩率。LZMA/LZMA2 算法在字典足够大时,对文本、日志、源码这类可压缩内容的压制效果明显好于 deflate,实测中”文本类内容比 zip 小 20%~40%”是常见的量级,具体比例取决于数据类型——已经压缩过的媒体文件(jpg/mp4/zip 本身)再压也不会小多少,这是信息论的边界,不是工具的锅。

7z 的功能也更现代:AES-256 加密是原生选项(加密文件名也可选,zip 的加密通常不隐藏文件名)、支持实心压缩(solid archive,把多个文件当连续数据流压,小文件多时压缩率提升明显)、分卷切分方便。

代价是兼容性:Windows 资源管理器至今不原生支持 7z 解压,对方机器上没装工具就打不开。所以 7z 的适用场景是”自用归档”和”双方都确认过工具链”的交付,不适合作为面向陌生人的通用分发格式。

另外提一句实心压缩的双刃剑:压缩率上去了,但单个文件的随机解压会变慢——要解第 1000 个文件,理论上得从实心块开头解过来。做按需取用的归档时要权衡这一点。

三、tar.gz / tar.zst:Linux 世界的交付语言

tar 本身不压缩,它是一个”把多个文件打包成一个连续数据流”的归档格式,保留 Unix 的权限位、属主、软链接。压缩靠外层的 gzip、xz 或 zstd。tar.gz 是源码分发的事实标准,tar.zst 在新一点的发行版里越来越常见(压缩/解压速度和压缩率的平衡更好)。

在 Linux 交付里 tar 的地位不可替代,原因有二:权限和符号链接能完整保留(zip 的权限支持很弱),以及解压工具开箱即用(tar -xzf 就完事)。

Windows 上处理 tar 要注意:Windows 10 之后自带 bsdtar(tar.exe),解压没问题,但打包时权限位和软链接的语义不一样——NTFS 的 ACL 不会映射成 Unix 权限,跨系统交付要留意这一点。把 Windows 上打的 tar.gz 发给 Linux 对方解压,权限可能变成 777 或全是默认值,写脚本的目录要注意可执行位丢失的问题。

另外 tar 对文件名编码也是”字节串”处理,中文名在 GBK 的 Windows 上打出来,到 UTF-8 的 Linux 上同样会乱。跨系统打包,文件名用 ASCII 是最省心的约定。

四、分卷与加密的注意点

大文件传输、网盘限容、U 盘拷贝,都会用到分卷(split)。zip 的分卷是”多磁盘”模式的遗留,实际使用中各家工具的兼容性参差;7z 的分卷(-v100m)实现统一,体验好得多;tar 侧一般用 split 命令切分再 cat 拼回,或者 zstd 的多流设计。

分卷的坑在于丢一卷就全废。分卷集合是一个整体,传输完成后最好核对卷数和总大小,或者干脆附一份校验值。

加密这件事,我把它分三档:

诉求 合适的做法 别用
防随手翻看 zip/7z 口令加密 不要当安全边界
传输中防未授权读取 7z AES-256 或专业加密容器 ZipCrypto
长期存储的机密 独立加密方案(如 age/VeraCrypt 类) 仅靠压缩包加密

压缩包加密的第一性问题是口令强度——AES-256 再强,口令是”123456″也白搭。另外要清楚:加密压缩包的密文是可被离线爆破的,文件被人拷走后安全性完全取决于口令熵值。它防的是”没有口令的人”,不防”有算力的人”。

五、Zip Slip:路径穿越这个老坑

2018 年被系统性披露的 Zip Slip 漏洞,本质是解压时没有校验归档里的路径。攻击者构造一个包含 ../../../../windows/system32/evil.dll 之类相对路径的压缩包,解压程序如果直接按条目路径写出文件,就会把文件写到目标目录之外——覆盖配置、投放可执行文件,都是从这里开始的。

防御思路在各种语言里都差不多:

  1. 解压前逐条目检查路径:拒绝 ..、拒绝绝对路径、拒绝盘符前缀。
  2. 规范化后确认目标路径仍在解压根目录之下(os.path.normpath / Path.resolve 之后做前缀判断)。
  3. 对条目类型保持警惕:符号链接条目本身可能指向目录外,某些归档里还有设备文件条目。
  4. 条目总大小和文件数设上限,防”压缩炸弹”(zip bomb)——一个 42.zip 可以展开到 TB 级,磁盘写满也是一种拒绝服务。

只要自己写解压代码,这几条是必查项。用成熟解压库时,也优先用它提供的”安全解压到目录”接口,而不是自己拼路径。

六、批量打包解包的脚本思路

批量处理压缩包的需求很实在:每天收几十个分包要汇总解压、项目目录要按周打包归档、日志要压缩转移。脚本化的骨架大致是:

# 批量解压到同名子目录,跳过已解压的
Get-ChildItem *.zip | ForEach-Object {
  $dest = Join-Path $_.Directory $_.BaseName
  if (Test-Path $dest) { return }
  Expand-Archive $_.FullName -DestinationPath $dest
}

要点有三个:目标目录冲突要处理(同名目录、重复解压)、失败要能续跑(跳过已完成项)、解压前做路径检查(上一节的教训)。批量场景下出一次问题就是成片的文件系统混乱,先小样本验证再全量跑,永远值得。

我自己的归档习惯是:对外交付用 zip,自用冷备份用 7z 分卷,跨 Linux 交付用 tar.gz,需要加密的走单独的加密容器。批量打包/解包这类重复操作,我现在基本在 7AA7工具箱里做——它带压缩包批量处理,选一堆包一键解压或打包,比手写脚本省事,也免得每次重新踩路径处理的坑。

格式选型没有唯一正确答案,搞清楚每个格式的设计初衷,按”兼容性、压缩率、功能、安全”四个维度排序,答案就出来了。

650浏览量
38赞
4评论
4参与用户
9 分钟阅读时间

评论(4)

  • North 北境 · 2026-10-03 19:50

    写得克制,没有夸大效果,这点很难得。

    回复
  • 凛川 Wave · 2026-10-04 02:50

    有没有考虑过性能开销?想了解量级。

    回复
  • AsyncLab · 2026-10-04 09:50

    看完顺手改了自己项目里的一段旧代码。

    回复
  • Maren · 2026-10-04 16:50

    这类内容值得做成系列。

    回复