Python zipfile.ZipFile.open 怎样按二进制流读取压缩成员
来源:17golang原创
时间:2026-11-02 13:04:08 340浏览 收藏
如果只想读取 ZIP 里的一个文件,zipfile.ZipFile.open() 比先解压到临时目录更直接:它按成员名或 ZipInfo 定位对象,返回一个可读取的二进制文件式对象。最稳妥的写法是把外层 ZipFile 和内层返回对象都放进 with,读取时按块消费,文本内容再单独解码。
官方文档:https://docs.python.org/3/library/zipfile.html#zipfile.ZipFile.open
记住三层边界:ZipFile管理压缩包,ZipFile.open()负责定位成员,返回的ZipExtFile负责提供二进制读取接口。不要把它直接当成已经解码好的字符串。
先区分 ZipFile、成员名和 ZipExtFile 的职责
ZipFile 是压缩包容器;name 参数是包内成员名,也可以传入一个 ZipInfo 对象;open() 返回的对象则是本次读取的流。默认读取模式是 r,读取对象按二进制文件接口工作。

这个划分很重要:包对象负责打开和关闭整个归档,流对象负责读取当前成员。文章中的“读取某个文件”实际上是先完成成员定位,再从流对象取得字节。
用 mode='r' 按块读取成员二进制流
对于图片、证书、压缩配置或未知编码的数据,先保留字节最安全。下面的示例只读取指定成员,不把完整压缩包一次性展开到磁盘:
from pathlib import Path
from zipfile import ZipFile
def read_member(zip_path: Path, member_name: str) -> bytes:
# 外层 with 负责关闭 ZIP 容器,内层 with 负责关闭成员流。
with ZipFile(zip_path, mode="r") as archive:
with archive.open(member_name, mode="r") as member:
chunks: list[bytes] = []
while True:
# 分块读取可以避免把大成员的全部内容一次压进内存。
chunk = member.read(64 * 1024)
if not chunk:
break
chunks.append(chunk)
return b"".join(chunks)
这里的 mode="r" 是读取压缩成员,返回对象支持 read()、readline()、迭代和定位等文件式操作。示例为了展示返回值使用了字节列表;如果文件可能很大,更适合在循环中直接把每个块写入另一个二进制流,而不是最后再拼接。
成员名不稳定时改用 ZipInfo
同一个 ZIP 中可能出现重复成员名。此时直接传字符串会让“按名称打开”变得不明确,可以先从 infolist() 得到具体的 ZipInfo,再把对象传给 open():
from zipfile import ZipFile, ZipInfo
def read_first_report(zip_path: str) -> bytes:
# 用 ZipInfo 保留具体条目,而不是只依赖可能重复的名称。
with ZipFile(zip_path, mode="r") as archive:
report: ZipInfo | None = next(
(item for item in archive.infolist() if item.filename.endswith("report.bin")),
None,
)
if report is None:
# 缺少目标成员时,把问题转换成调用方可处理的异常。
raise FileNotFoundError("ZIP 中没有 report.bin")
with archive.open(report, mode="r") as member:
# read() 只负责取得字节,解码或解析应由上一层决定。
return member.read()
如果成员名不存在,定位阶段通常会抛出 KeyError;如果业务希望统一错误语义,可以像示例一样先列举成员并主动抛出更明确的异常。这里仍然没有声称“读取成功”或展示运行输出,代码只是说明调用关系。
把读取、解码和资源关闭分开
ZIP 成员是二进制流,文本文件要在流外再加一层解码。不要用字符串方法替代字节读取,也不要假设所有成员都是 UTF-8:

import io
from zipfile import ZipFile
def read_text_member(zip_path: str, member_name: str, encoding: str = "utf-8") -> str:
# ZipFile.open() 先给出二进制流,再由 TextIOWrapper 负责字符解码。
with ZipFile(zip_path, mode="r") as archive:
with archive.open(member_name, mode="r") as binary_member:
with io.TextIOWrapper(binary_member, encoding=encoding) as text_member:
# 文本层只消费 str,编码错误由调用方按业务策略处理。
return text_member.read()
关闭顺序由嵌套的 with 管理,调用方不必手写多个 close()。外层 ZipFile 关闭后,不应继续使用成员流;官方文档把这种情况定义为 ValueError。这也是把读取逻辑放在上下文块内部的原因。
发布前记住这几个边界
| 场景 | 建议 | 原因 |
|---|---|---|
| 未知内容类型 | 先按 bytes 读取 | 避免错误假设字符编码 |
| 成员名可能重复 | 使用 ZipInfo | 保留具体归档条目 |
| 成员很大 | 循环分块消费 | 控制单次内存占用 |
| 文本成员 | 显式指定编码再解码 | 把数据读取与字符转换分离 |
| 资源生命周期 | 嵌套使用 with | 避免容器或流泄漏 |
相关问题
为什么不直接用 zipfile.Path?
zipfile.Path 更适合把压缩包成员组织成路径式对象;本篇关注的是明确调用 ZipFile.open() 后得到二进制流,并控制读取、解码和关闭边界。两者解决的问题不同。
读取 ZIP 文本时能不能直接得到字符串?
ZipFile.open() 的读取对象是二进制文件式对象。应先读取字节,再使用 io.TextIOWrapper 或 bytes.decode() 按已知编码转换,不能把默认编码当成文件事实。
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 485次学习