本文要点
要观察ZipInfo字段,先得有一个自己造的压缩包
拿到压缩包后,用getinfo取单个成员,再读它的两个大小字段
如果你想知道压缩包里每个成员的大小,而不是某一个,用infolist()

本文目录
先明确:getinfo返回的是ZipInfo,不是文件内容
如果你手里有一个ZIP压缩包,想在不解压的情况下知道里面某个成员有多大,用ZipFile.getinfo(name)就能拿到一个ZipInfo对象。
按zipfile模块文档的说明,getinfo(name)返回一个ZipInfo对象,里面存的是压缩包成员name的信息。如果这个名字在压缩包里不存在,会抛出KeyError。
这里要分清两件事:getinfo只读元数据,不打开成员内容、不解压、不写磁盘。它和read()、extract()是不同用途的方法。本文只做读取ZipInfo字段这一件事,不提取任何成员,也不比较不同压缩方法的速度。
适用情境(自建示例):你在练习目录里自己打包一个ZIP,然后用getinfo查看其中某个成员的大小。下面所有路径和文件都是练习用的自建示例,不是对真实压缩包的记录。动手前请把练习文件放在单独目录,例如exercise/,不要拿重要压缩包直接试。
一个边界:本文只读取你自己创建或可信来源的示例压缩包。不要对来源不明的压缩包执行解压操作。
自建一个练习ZIP:先造两个大小不同的成员
要观察ZipInfo字段,先得有一个自己造的压缩包。下面这段代码只在练习目录里创建文件并打包,不碰系统其他位置。
import zipfile
from pathlib import Path
base = Path("exercise")
base.mkdir(exist_ok=True)
# 造两个自建成员,内容长度不同
(base / "small.txt").write_text("abc", encoding="utf-8")
(base / "big.txt").write_text("x" * 500, encoding="utf-8")
zip_path = base / "demo.zip"
with zipfile.ZipFile(zip_path, "w", compression=zipfile.ZIP_DEFLATED) as zf:
zf.write(base / "small.txt", arcname="small.txt")
zf.write(base / "big.txt", arcname="big.txt")这段代码用mode="w"新建压缩包,compression指定写入时使用的压缩方法。文档说明,compression应是ZIP_STORED、ZIP_DEFLATED、ZIP_BZIP2、ZIP_LZMA或ZIP_ZSTANDARD之一;如果指定了ZIP_DEFLATED但zlib模块不可用,会抛出RuntimeError。
注意arcname:write()的arcname参数决定成员在压缩包里的名字。不指定时默认用文件名,但会去掉盘符和前导路径分隔符。这里显式写成small.txt和big.txt,方便后面按名字取。
运行后练习目录里会多出一个demo.zip。它只是练习产物,可以随时删掉重来。
用getinfo读file_size和compress_size:两个大小含义不同
拿到压缩包后,用getinfo取单个成员,再读它的两个大小字段。
import zipfile
with zipfile.ZipFile("exercise/demo.zip") as zf:
info = zf.getinfo("big.txt")
print(info.filename)
print(info.file_size)
print(info.compress_size)按文档说明,ZipInfo.file_size是未压缩文件的大小,ZipInfo.compress_size是压缩后数据的大小。这两个值回答的是不同问题:file_size告诉你这个成员解压后有多大,compress_size告诉你它在压缩包里占多少字节。
所以“读取ZIP成员的大小”这句话本身有歧义,要先决定你要的是哪一个。想估算解压后占用空间,看file_size;想看它在压缩包里占的位置,看compress_size。
对上面自建的big.txt来说,file_size是500,因为写入的是500个字符x,按UTF-8编码每个占1字节。compress_size通常小于500,因为重复字符容易被压缩。具体数值取决于你实际写入的内容和压缩方法,本文不给出固定数字。
一个提醒:file_size是元数据里记录的值,不是解压后实测的结果。本文不提取成员,所以不对它做验证。
批量列出所有成员大小:infolist和namelist怎么配合
如果你想知道压缩包里每个成员的大小,而不是某一个,用infolist()。
按文档说明,infolist()返回一个列表,列表里每个元素是对应成员的ZipInfo对象;如果打开的是已存在的压缩包,这些对象的顺序和它们在磁盘上实际ZIP文件里的条目顺序一致。
import zipfile
with zipfile.ZipFile("exercise/demo.zip") as zf:
for info in zf.infolist():
print(info.filename, info.file_size, info.compress_size)这段代码只打印,不写文件、不解压。输出里每一行是一个成员的名字、未压缩大小、压缩后大小。
另一个方法是namelist(),它只返回成员名字的列表,不返回大小。所以如果你既要名字又要大小,用infolist()一次拿全;如果只需要名字,namelist()更直接。
注意顺序:文档说infolist()的顺序和磁盘上条目顺序一致,但这不是按大小或名字排好的。需要按大小排序时,自己用sorted处理,不要假设列表已经有序。
名字写错会怎样:KeyError和is_dir的检查
getinfo对名字是敏感的。文档明确说,对一个当前不在压缩包里的名字调用getinfo()会抛出KeyError。
所以排查顺序可以这样定:
1. 先用namelist()打印所有成员名字,确认你要找的名字到底写成什么样。大小写、路径分隔符、有没有子目录前缀,都会影响匹配。
2. 用打印出来的名字原样传给getinfo()。
3. 如果仍然报KeyError,说明这个名字确实不在压缩包里,不要靠猜补全。
另一个容易踩的点是目录条目。文档说明ZipInfo.is_dir()根据条目名字判断是不是目录,目录名应以斜杠结尾。也就是说,压缩包里可能有一个名字以斜杠结尾的目录条目,它也有自己的ZipInfo。对它读file_size通常没有实际文件大小的意义。
判断方法:拿到info后先调用info.is_dir(),返回True就说明这是目录条目,跳过它的大小读取。
下面这段只做检查,不修改压缩包:
import zipfile
with zipfile.ZipFile("exercise/demo.zip") as zf:
for info in zf.infolist():
if info.is_dir():
print(info.filename, "目录条目")
else:
print(info.filename, info.file_size)使用限制:只读元数据,不提取不可信文件
本文的方法只做一件事:读取ZipInfo里的字段。它不解压、不执行、不修改压缩包内容。
几条需要记住的限制:
1. 不要对来源不明的压缩包执行extract或extractall。文档在extractall()的警告里写明:永远不要在没有事先检查的情况下解压不可信来源的压缩包,因为成员可能带有绝对路径或含“..”的文件名,导致文件被创建到目标路径之外。本文只读元数据,正是为了避开这一步。
2. getinfo和infolist读的是压缩包记录的元数据,不是解压后的实测结果。file_size和compress_size来自压缩包自身,本文不对它们做校验。
3. 本文不比较不同压缩方法的速度,也不给性能结论。文档只说明各压缩方法需要对应模块,没有给出速度对比数据。
4. 加密成员的处理不在本文范围内。文档提到zipfile支持解密加密文件,但解密用原生Python实现,速度很慢;本文不涉及解密操作。
5. 版本差异:ZipInfo的公开compress_level属性在3.13加入,此前是受保护的_compresslevel。本文用到的filename、file_size、compress_size、is_dir()不涉及这个变化。
参考资料:Python官方zipfile模块文档,https://docs.python.org/3/library/zipfile.html。
观看知识要点视频 18 秒
18秒无旁白要点导览,文字来自已审核正文;完整步骤、条件及参考资料见下方。
参考资料
以下原始资料提供本文依据;请结合日期、适用条件和产品说明查阅。
请结合原始资料的适用条件阅读。
返回文件整理 →