登录
推荐 文章 Go 技术 课程 下载 专题 AI
首页 >  文章 >  python教程

Python zipfile 怎样读取压缩包目录而不解压

来源:17golang原创

时间:2026-11-08 03:13:11 423浏览 收藏

如果只想确认 ZIP 里有哪些文件、目录层级和大小,不需要先把整个压缩包解到磁盘。Python 标准库的 zipfile 可以打开归档并读取中央目录:用 namelist() 拿到成员名称,用 infolist() 拿到更完整的 ZipInfo 元数据;只有调用 read() 或 extractall() 等操作时,才进入文件内容读取或解压阶段。

官方文档:https://docs.python.org/3/library/zipfile.html

读取压缩包目录的最小写法是用 with zipfile.ZipFile(path) as archive 打开文件,再遍历 archive.namelist()。它只返回归档成员名称,不会把成员文件释放到当前目录。

先用 namelist() 读取压缩包目录

namelist() 返回归档中成员名称组成的列表,适合先做文件存在性判断、目录前缀筛选和简单目录展示。使用上下文管理器可以让读取目录结束后自动关闭 ZIP 文件:

from pathlib import Path
from zipfile import BadZipFile, ZipFile

archive_path = Path("release.zip")

try:
    # 这里只读取 ZIP 的成员名称,不把任何成员解压到磁盘。
    with ZipFile(archive_path) as archive:
        names = archive.namelist()
        for name in names:
            # 名称可能以 / 结尾,表示归档中的目录条目。
            print(name)
except BadZipFile:
    # 文件存在但不是可读取的 ZIP 时,给调用方保留明确错误边界。
    print("输入文件不是有效的 ZIP 归档")

这里的关键是 ZipFile 负责打开归档,namelist() 负责返回名称。列表中的名称仍然是归档内部路径,不是已经创建在本机文件系统上的路径,因此不能把它们当成解压结果。

ZipFile、中央目录和 namelist 文件名列表的静态关系图
图1:ZipFile 从压缩包中央目录取得成员名称;这是静态结构说明图,不是运行截图。

用 infolist() 读取条目元数据

如果还要判断某个成员是不是目录,或者比较原始大小与压缩后大小,可以遍历 infolist()。它返回 ZipInfo 对象,每个对象对应一个归档成员:

from zipfile import ZipFile

with ZipFile("release.zip") as archive:
    for info in archive.infolist():
        # is_dir() 判断归档条目是否表示目录,避免只靠字符串猜测。
        kind = "目录" if info.is_dir() else "文件"
        print(
            f"{kind}: {info.filename} | "
            f"原始 {info.file_size} bytes | 压缩后 {info.compress_size} bytes"
        )

filename 是归档中的成员名,file_size 是未压缩大小,compress_size 是压缩数据大小。它们都是目录元数据,读取这些字段不等于读取成员正文。对于旧式或特殊归档,也不要假设每个目录都一定有一个显式的“目录条目”;判断具体文件时,用名称前缀和业务规则补充处理更稳妥。

ZipInfo 条目与 filename、is_dir、file_size、compress_size 字段的关系图
图2:infolist() 返回 ZipInfo 条目,目录判断与大小字段都来自条目元数据;这是静态结构说明图。

打开前先判断输入是不是 ZIP

文件名后缀不能完全代表文件格式。如果输入来自上传、批处理或用户选择,先用 is_zipfile() 做轻量判断,再进入 ZipFile 读取流程,可以把普通文件和损坏归档分开处理:

from pathlib import Path
from zipfile import BadZipFile, ZipFile, is_zipfile

path = Path("incoming.data")

if not is_zipfile(path):
    # 这个判断只说明文件不符合 ZIP 文件特征,不负责修复或解压文件。
    raise ValueError(f"不是 ZIP 文件: {path}")

try:
    with ZipFile(path) as archive:
        # 目录读取仍然只访问成员清单,不读取每个成员的正文。
        for name in archive.namelist():
            print(name)
except BadZipFile as exc:
    # 处理目录结构损坏等打开错误,避免把异常伪装成空目录。
    raise ValueError("ZIP 目录无法读取") from exc

is_zipfile() 适合做输入筛选,但它不是内容安全审计,也不能保证每个成员都能成功解压。真正打开时仍要处理 BadZipFile,不要在异常后返回一个“没有文件”的空列表。

区分目录查看、读取内容和实际解压

这几个接口名称相近,但资源成本和结果不同:

操作得到什么是否把成员释放到磁盘
namelist()成员名称列表不会
infolist()名称、大小、时间等 ZipInfo 元数据不会
printdir()直接把目录表打印到标准输出不会
read(name)指定成员的解压后字节内容不会写入文件,但会读取内容
extractall(path)把成员解压到目标目录会

因此,做“压缩包里有没有某个配置文件”的判断时,先用 namelist() 或 infolist();只有确实需要解析文件内容时,再调用 read()。不要为了列目录而执行 extractall()。

命令行也可以只列目录

不写 Python 脚本时,标准库自带的命令行接口可以直接列出 ZIP 文件成员:

# -l 只列出 ZIP 目录,不把成员解压到目标目录。
python -m zipfile -l release.zip

命令行方式适合人工快速查看;需要根据目录前缀筛选、读取大小字段或接入上传流程时,再使用 Python API。二者都应该把“查看目录”和“读取内容”分开。

处理不可信压缩包时不要直接解压

只读取目录通常比直接解压更容易控制,但目录名称本身也应该被当作外部输入。对于来源不明的归档,不要因为列出了某个文件就认为内容可信;如果后续要执行 read() 或解压,应设置文件数量、单文件大小、总大小和处理时间等业务限制,并把输出目录固定在受控位置。

尤其要注意,官方文档对 extract() 和 extractall() 提醒过路径、文件系统容量和压缩炸弹等风险。本文的示例停留在目录读取阶段,不能替代对实际解压流程的安全设计。

常见问题

namelist() 会把压缩包解压吗?

不会。它返回归档成员名称;如果要得到某个成员的实际字节内容,应调用 read(),如果要生成本地文件,则应使用提取接口。

为什么只看文件后缀不够?

后缀是命名约定,不是文件格式判断。上传或批处理输入可以先调用 is_zipfile(),打开时再捕获 BadZipFile。

如何判断一个条目是目录?

使用 infolist() 得到 ZipInfo 后调用 info.is_dir()。同时保留对名称前缀的业务判断,因为不同归档不一定显式保存全部目录条目。

总结

Python zipfile 读取压缩包目录的核心是:用 ZipFile 打开归档,用 namelist() 读取成员名称,用 infolist() 读取 ZipInfo 元数据。is_zipfile() 和 BadZipFile 负责输入边界,read() 与 extractall() 则分别进入内容读取和实际解压阶段。把这些职责分开,既能减少不必要的磁盘写入,也能让后续的安全限制更容易落地。

声明:本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>