← 返回Python标准库精讲目录
← 返回学习笔记首页
专题: Python标准库精讲系统学习
关键词: Python, 标准库, zipfile, ZIP, 压缩, 解压, ZipFile, ZipInfo, extract, 归档
一、ZIP格式概述
ZIP是一种广泛应用于文件归档和压缩的格式,由Phil Katz于1989年发明,现已成为互联网上最流行的归档格式之一。Python标准库中的zipfile模块提供了创建、读取、写入、追加和解压ZIP文件的完整工具集,无需依赖任何外部程序。
zipfile模块支持多种压缩算法,包括传统的存储(不压缩)方式、DEFLATE算法、bzip2算法以及LZMA算法,能够灵活应对不同的使用场景。该模块同时支持ZIP64扩展,可以处理超过4GB的大型ZIP文件。
在Python生态中,zipfile是处理ZIP归档的标准方案。相比第三方库如patool或py7zr,zipfile作为内置模块无需额外安装,开箱即用。与shutil.make_archive相比,zipfile提供了更细粒度的控制能力,适合需要精确操作ZIP文件内容的场景。
官方文档:zipfile模块是处理ZIP归档(一种流行的文件打包和压缩标准)的瑞士军刀,支持归档的创建、读取、写入、追加和列出操作。
ZIP格式的核心特征是将多个文件打包为单个归档文件,同时可选择对每个文件进行压缩。每个文件在ZIP归档中独立存储其元数据,包括文件名、修改时间、CRC32校验和以及压缩前后的大小信息,这使得随机访问归档中的单个文件成为可能,而无需解压整个归档。
二、ZipFile类的模式
ZipFile是zipfile模块的核心类,用于创建和操作ZIP文件。其构造函数的第二个参数mode指定打开模式,不同模式对应不同的操作场景。
2.1 模式一览
ZipFile支持五种模式:'r'读取、'w'写入、'a'追加、'x'排他创建,以及带上下文管理器的自动管理方式。
模式 含义 文件存在时 文件不存在时
'r' 读取(默认) 读取内容 抛出FileNotFoundError
'w' 写入 覆盖原有内容 创建新文件
'a' 追加 在末尾追加新文件 创建新文件
'x' 排他创建 抛出FileExistsError 创建新文件
2.2 读取模式('r')
读取模式是默认模式,用于打开现有的ZIP文件进行读取操作。在此模式下可以浏览归档中的文件列表、读取文件内容、提取文件等。
import zipfile
# 打开现有ZIP文件(默认模式为'r')
with zipfile.ZipFile('example.zip' , 'r' ) as zf:
# 列出所有文件名
print(zf.namelist())
# 读取特定文件的内容
content = zf.read('file.txt' )
print(content.decode('utf-8' ))
2.3 写入模式('w')
写入模式用于创建新的ZIP文件。如果指定的文件已经存在,会被覆盖。在写入模式下,可以使用write()和writestr()方法向归档中添加文件。
# 创建新ZIP文件(覆盖已有文件)
with zipfile.ZipFile('archive.zip' , 'w' ) as zf:
# 添加磁盘上的文件
zf.write('readme.txt' , arcname='doc/readme.txt' )
# 直接写入字符串内容
zf.writestr('config.json' , '{"version": "1.0"}' )
2.4 追加模式('a')
追加模式用于在现有ZIP文件中添加新文件或修改已有内容。如果文件不存在,则会创建新文件。这在分批构建归档文件时非常有用。
# 向现有ZIP文件追加内容
with zipfile.ZipFile('archive.zip' , 'a' ) as zf:
zf.write('new_data.csv' , arcname='data/new_data.csv' )
2.5 排他创建模式('x')
排他创建模式类似于'w'模式,但如果文件已存在,会抛出FileExistsError,从而避免意外覆盖已有文件。这在需要确保不会丢失已有归档内容时很有用。
# 仅当文件不存在时创建
try :
with zipfile.ZipFile('important.zip' , 'x' ) as zf:
zf.write('critical_data.txt' )
except FileExistsError:
print('文件已存在,不会被覆盖' )
2.6 上下文管理器
ZipFile支持上下文管理器协议(with语句),可以自动管理文件的打开和关闭。使用上下文管理器是最佳实践,可以确保即使在发生异常时文件也能被正确关闭,避免资源泄漏。
# 上下文管理器自动处理close()
with zipfile.ZipFile('data.zip' , 'r' ) as zf:
data = zf.read('data.txt' )
# 离开with块后,文件自动关闭
在Python 3.11及更高版本中,ZipFile还可以作为可关闭的上下文管理器,在异常处理时正确清理资源。如果使用旧版本Python,建议始终使用try/finally或with语句来确保资源释放。
三、压缩方法
zipfile模块支持多种压缩算法,通过compression参数指定。不同的压缩算法在压缩率、速度和内存消耗方面各有优劣,应根据实际场景选择。
3.1 压缩方法常量
常量 对应算法 压缩率 速度 Python版本支持
ZIP_STORED 仅存储,不压缩 无 极快 全版本
ZIP_DEFLATED DEFLATE算法 中等 较快 全版本
ZIP_BZIP2 bzip2算法 较高 中等 Python 3.3+
ZIP_LZMA LZMA算法 最高 较慢 Python 3.3+
3.2 ZIP_STORED(存储模式)
ZIP_STORED模式不对文件进行任何压缩,仅将文件原样打包进ZIP归档中。适合打包已经压缩过的文件(如JPEG、MP4等),因为对这些文件再次压缩不会减小体积,反而浪费CPU时间。
import zipfile
# 仅打包,不压缩
with zipfile.ZipFile('images.zip' , 'w' ,
compression=zipfile.ZIP_STORED) as zf:
zf.write('photo.jpg' )
zf.write('video.mp4' )
3.3 ZIP_DEFLATED(DEFLATE压缩)
ZIP_DEFLATED使用DEFLATE算法进行压缩,是ZIP文件最常用的压缩方法,在压缩率和速度之间取得了良好平衡。DEFLATE也是gzip和PNG等格式使用的压缩算法。
# 使用DEFLATE压缩(最常用)
with zipfile.ZipFile('documents.zip' , 'w' ,
compression=zipfile.ZIP_DEFLATED) as zf:
zf.write('report.docx' )
zf.write('summary.pdf' )
3.4 ZIP_BZIP2(bzip2压缩)
ZIP_BZIP2使用bzip2算法,通常能获得比DEFLATE更高的压缩率,但压缩速度较慢。适用于对压缩率要求较高且对压缩时间不太敏感的场景。
# 使用bzip2压缩(更高压缩率)
with zipfile.ZipFile('logs.zip' , 'w' ,
compression=zipfile.ZIP_BZIP2) as zf:
zf.write('app.log' )
zf.write('error.log' )
3.5 ZIP_LZMA(LZMA压缩)
ZIP_LZMA使用LZMA算法(7-Zip的压缩核心),提供最高的压缩率,但压缩速度最慢。适用于需要最大程度缩减归档体积且对时间要求宽松的场景。解压速度相对较快,与压缩速度形成鲜明对比。
# 使用LZMA压缩(最高压缩率)
with zipfile.ZipFile('archive_max.zip' , 'w' ,
compression=zipfile.ZIP_LZMA) as zf:
zf.write('large_file.txt' )
3.6 compresslevel参数
对于ZIP_DEFLATED、ZIP_BZIP2和ZIP_LZMA这三种方法,可以通过compresslevel参数控制压缩级别。压缩级别越高,压缩率越大,但耗时也越长。不同算法的有效范围不同:DEFLATE为0-9(默认6),bzip2为1-9(默认9),LZMA为0-9(默认9)。
# 使用不同的压缩级别
with zipfile.ZipFile('fast.zip' , 'w' ,
compression=zipfile.ZIP_DEFLATED,
compresslevel=1 ) as zf:
zf.write('large_data.bin' )
# 最高压缩级别
with zipfile.ZipFile('max.zip' , 'w' ,
compression=zipfile.ZIP_DEFLATED,
compresslevel=9 ) as zf:
zf.write('large_data.bin' )
选择建议: 常规使用ZIP_DEFLATED(默认即可);归档已压缩文件用ZIP_STORED以节省CPU;追求极限压缩用ZIP_LZMA;对压缩率要求较高的文本文件可用ZIP_BZIP2。
四、文件操作
zipfile模块提供了丰富的文件操作方法,涵盖从归档中添加文件到从归档中提取文件的完整操作链路。
4.1 添加文件 — write()
write()方法将磁盘上的文件添加到ZIP归档中。arcname参数可以指定归档内部的路径名,如果不指定则使用原始文件名。compress_type参数可以为单个文件指定不同的压缩方法。
# write()方法详解
with zipfile.ZipFile('backup.zip' , 'w' ,
compression=zipfile.ZIP_DEFLATED) as zf:
# 基本用法:添加当前目录下的文件
zf.write('data.csv' )
# 指定归档路径:在ZIP中以不同名称存储
zf.write('D:\\data\\raw\\input.csv' , arcname='raw/input.csv' )
# 为单个文件指定不同压缩方式
zf.write('photo.jpg' , compress_type=zipfile.ZIP_STORED)
4.2 直接写入内容 — writestr()
writestr()方法直接将字符串或字节数据写入ZIP归档中的文件,不需要先在磁盘上创建文件。这在处理程序运行时生成的数据(如JSON、CSV、日志等)时特别方便。
# writestr()直接写入字符串内容
with zipfile.ZipFile('config.zip' , 'w' ) as zf:
zf.writestr('database.ini' ,
'[database]\nhost=localhost\nport=5432\n' )
zf.writestr('config.json' ,
'{"app_name": "MyApp", "version": "2.0"}' )
# 写入字节数据
zf.writestr('binary.dat' , b'\x00\x01\x02\x03' )
4.3 提取文件 — extract()
extract()方法从ZIP归档中提取单个文件到指定目录。path参数指定提取到的目标目录(默认为当前目录),members参数可以指定要提取的文件列表。如果归档中的文件名包含路径,会自动创建目录结构。
# 提取单个文件
with zipfile.ZipFile('archive.zip' , 'r' ) as zf:
# 提取到当前目录
zf.extract('doc/readme.txt' )
# 提取到指定目录
zf.extract('doc/readme.txt' , path='./output' )
4.4 提取所有文件 — extractall()
extractall()方法提取ZIP归档中的所有文件到指定目录。可以指定一个成员列表来提取部分文件。注意:在提取不可信来源的ZIP文件时,理论上存在路径遍历攻击的风险(文件名包含"../"跳转到父目录),建议始终使用最新版本的Python,它内置了路径验证。
# 提取所有文件
with zipfile.ZipFile('full_backup.zip' , 'r' ) as zf:
# 提取全部
zf.extractall('./restored' )
# 提取部分文件
zf.extractall('./docs' , members=['doc/file1.txt' ,
'doc/file2.txt' ])
4.5 查询文件列表 — namelist()
namelist()返回归档中所有文件和目录名称的列表,是最常用的归档内容浏览方法。每个条目对应于归档文件头中记录的路径名。
# 列出归档内容
with zipfile.ZipFile('project.zip' , 'r' ) as zf:
for name in zf.namelist():
print(name)
4.6 获取详细信息 — infolist() 和 getinfo()
infolist()返回归档中所有文件的ZipInfo对象列表,每个对象包含文件的完整元数据。getinfo()则返回单个文件的ZipInfo对象,适用于已知文件名的精确查询。
# infolist()获取所有文件信息
with zipfile.ZipFile('data.zip' , 'r' ) as zf:
for info in zf.infolist():
print(f'{info.filename} 原始大小:{info.file_size}' )
# getinfo()获取单个文件信息
with zipfile.ZipFile('data.zip' , 'r' ) as zf:
info = zf.getinfo('specific_file.txt' )
print(info)
五、ZipInfo信息
ZipInfo是一个描述ZIP归档中单个文件元数据的类。通过ZipInfo对象可以获取文件的详细信息,包括大小、压缩方式、时间戳等。ZipInfo由infolist()和getinfo()方法返回,也可以在创建ZIP文件时自定义。
5.1 核心属性
属性 类型 说明
filename str 归档中的文件名
file_size int 原始文件大小(字节)
compress_size int 压缩后的大小(字节)
compress_type int 压缩方法对应的整数常量
date_time tuple 文件修改时间 (年,月,日,时,分,秒)
CRC int 文件的CRC32校验和
comment bytes 单个文件的注释
extra bytes 扩展字段数据
extract_version int 解压所需的最低版本
flag_bits int ZIP标志位
volume int 分卷编号
internal_attr int 内部属性
external_attr int 外部属性(含权限位)
header_offset int 文件头偏移量
file_offset int 文件数据偏移量
5.2 查看文件元信息
通过ZipInfo可以深入分析归档中每个文件的详细信息,这在验证归档完整性、分析压缩效率等场景中非常有用。
# 详细查看归档内容
with zipfile.ZipFile('analysis.zip' , 'r' ) as zf:
for info in zf.infolist():
# 计算压缩率
ratio = info.compress_size / info.file_size if info.file_size > 0 else 0
# 格式化时间
dt = info.date_time
timestamp = f'{dt[0]}-{dt[1]:02d}-{dt[2]:02d} ' \
f'{dt[3]:02d}:{dt[4]:02d}:{dt[5]:02d}'
print(f'{info.filename}' )
print(f' 原始大小: {info.file_size:,} 字节' )
print(f' 压缩大小: {info.compress_size:,} 字节' )
print(f' 压缩率: {ratio:.1%}' )
print(f' 修改时间: {timestamp}' )
print(f' CRC32: {info.CRC:08x}' )
print(' ---' )
5.3 自定义ZipInfo
在向ZIP归档写入文件时,也可以创建自定义的ZipInfo对象来控制文件的元数据,比如修改时间、注释等。
# 创建自定义ZipInfo
import zipfile
from datetime import datetime
with zipfile.ZipFile('custom.zip' , 'w' ) as zf:
# 创建ZipInfo对象,指定文件名和修改时间
info = zipfile.ZipInfo(
filename='hello.txt' ,
date_time=(2026 , 5 , 5 , 12 , 0 , 0 )
)
info.comment = 'This is a custom file' .encode('utf-8' )
# 使用ZipInfo对象写入内容
zf.writestr(info, 'Hello, World!' )
六、密码与加密
zipfile模块支持对ZIP文件设置密码保护,但需要注意其加密机制是基于传统的ZIP 2.0加密(PKWARE传统加密),而非更安全的AES加密。这一限制意味着zipfile提供的加密强度有限,适合基本的安全防护而非高度敏感数据。
6.1 用密码打开ZIP文件
可以通过ZipFile构造函数的pwd参数或在打开后调用setpassword()方法来设置解密密码。所有后续的提取操作会自动使用该密码进行解密。
# 方法一:在构造函数中指定密码
with zipfile.ZipFile('protected.zip' , 'r' ,
pwd='secret123' .encode('utf-8' )) as zf:
zf.extractall('./decrypted' )
# 方法二:使用setpassword()设置密码
with zipfile.ZipFile('protected.zip' , 'r' ) as zf:
zf.setpassword('my_password' .encode('utf-8' ))
content = zf.read('secret.txt' )
print(content.decode('utf-8' ))
6.2 创建加密的ZIP文件
在写入模式下,同样可以通过pwd参数或setpassword()设置密码。在Python 3.6及更高版本中,zipfile在写入时支持设置密码,使用ZIP 2.0传统加密。
# 创建带密码保护的ZIP文件
with zipfile.ZipFile('encrypted.zip' , 'w' ,
pwd='p@ssw0rd' .encode('utf-8' )) as zf:
zf.write('confidential.docx' )
zf.writestr('notes.txt' , 'This is encrypted content.' )
6.3 密码保护的局限性
zipfile模块的加密基于ZIP 2.0的PKWARE传统加密算法,存在以下局限性:
加密强度有限 :PKWARE传统加密算法已被证明不够安全,专业的密码破解工具可以在较短时间内破解弱密码。
不支持AES加密 :WinZIP和7-Zip支持的AES-256加密,zipfile标准模块无法创建或读取。如需AES加密,需使用pyzipper或pyminizip等第三方库。
仅加密文件内容 :传统加密只加密文件数据,文件列表(文件名)仍然是明文可见的,这意味着即使设置了密码,其他人仍能看到归档中包含了哪些文件。
密码为字节串 :pwd参数需要传入bytes类型。如果密码包含非ASCII字符,需要特别注意编解码方式。
安全建议: 对于需要真正安全加密的场景,建议使用专门的加密库(如cryptography)先对数据进行加密,再存入ZIP归档;或者使用支持AES-256的第三方库如pyzipper。
6.4 暴力破解防护思路
虽然zipfile不提供暴力破解防护,但在实际应用中,可以通过以下方式增强安全性:
# 使用强密码和迭代加密(双重保护)
import hashlib, secrets, string
# 生成强随机密码
alphabet = string.ascii_letters + string.digits + '!@#$%^&*'
password = '' .join(secrets.choice(alphabet) for _ in range(20 ))
print(f'生成的强密码: {password}' )
# 创建加密ZIP
with zipfile.ZipFile('secure.zip' , 'w' ,
pwd=password.encode('utf-8' )) as zf:
zf.write('sensitive_data.xlsx' )
七、实战案例与总结
通过以下几个实战案例,展示zipfile模块在实际开发中的典型应用场景。
7.1 案例一:目录批量压缩
将指定目录下的所有文件递归打包到ZIP归档中,并保持目录结构。这个函数可以嵌入到备份脚本或批量文件导出工具中使用。
import zipfile
import os
from pathlib import Path
def zip_directory (source_dir: 'str' , output_zip: 'str' ,
compression=zipfile.ZIP_DEFLATED) -> 'int' :
"""将目录递归压缩为ZIP文件,返回文件计数"""
source = Path(source_dir)
count = 0
with zipfile.ZipFile(output_zip, 'w' ,
compression=compression) as zf:
for file_path in source.rglob('*' ):
if file_path.is_file():
# 计算归档中的相对路径
arcname = str(file_path.relative_to(source))
zf.write(file_path, arcname=arcname)
count += 1
return count
# 使用示例
count = zip_directory('./my_project' , 'my_project_backup.zip' )
print(f'已压缩 {count} 个文件' )
7.2 案例二:带进度条解压
在解压大文件时显示进度,方便用户了解当前解压进度。这个案例演示了如何在提取过程中提供实时反馈。
import zipfile
def extract_with_progress (zip_path: 'str' , extract_dir: 'str' ):
"""带进度条的解压函数"""
with zipfile.ZipFile(zip_path, 'r' ) as zf:
members = zf.infolist()
total = len(members)
for idx, member in enumerate(members, 1 ):
zf.extract(member, path=extract_dir)
# 打印进度百分比
pct = idx / total * 100
print(f'\r解压进度: [{pct:5.1f}%] {member.filename}' ,
end='' , flush=True )
print('\n解压完成!' )
# 使用示例
# extract_with_progress('large_archive.zip', './output')
7.3 案例三:ZIP文件完整性检查
对ZIP归档中的所有文件进行完整性校验,通过比对CRC32校验和和数据完整性来确保归档未被损坏。这对备份恢复前的检查尤为重要。
import zipfile
import zlib
def verify_zip_integrity (zip_path: 'str' ) -> 'list[str]' :
"""检查ZIP文件完整性,返回损坏的文件列表"""
corrupted = []
with zipfile.ZipFile(zip_path, 'r' ) as zf:
for info in zf.infolist():
try :
# 读取文件内容并自动校验CRC
data = zf.read(info.filename)
# 手动验证CRC32(read()内部已做,此处为示范)
expected_crc = info.CRC
actual_crc = zlib.crc32(data) & 0xFFFFFFFF
if actual_crc != expected_crc:
corrupted.append(info.filename)
print(f'[损坏] {info.filename}' )
else :
print(f'[通过] {info.filename}' )
except Exception as e:
corrupted.append(info.filename)
print(f'[错误] {info.filename}: {e}' )
return corrupted
# 使用示例
bad_files = verify_zip_integrity('backup.zip' )
if not bad_files:
print('✓ 所有文件完整性校验通过' )
else :
print(f'✗ 发现 {len(bad_files)} 个损坏文件' )
7.4 案例四:内存中的ZIP操作
使用BytesIO可以在内存中创建和读取ZIP文件,而无需写入磁盘。这对于网络传输或临时处理数据非常有用。
import zipfile
from io import BytesIO
# 在内存中创建ZIP
buffer = BytesIO()
with zipfile.ZipFile(buffer, 'w' ,
compression=zipfile.ZIP_DEFLATED) as zf:
zf.writestr('file1.txt' , 'Content of file 1' )
zf.writestr('file2.txt' , 'Content of file 2' )
# 在内存中读取ZIP
buffer.seek(0 )
with zipfile.ZipFile(buffer, 'r' ) as zf:
print(zf.namelist())
for name in zf.namelist():
print(f'{name}: {zf.read(name).decode("utf-8")}' )
7.5 核心要点总结
zipfile模块核心要点:
1. ZipFile类的四种模式:'r'读取、'w'写入(覆盖)、'a'追加、'x'排他创建,建议始终使用上下文管理器(with语句)。
2. 四种压缩方法:ZIP_STORED(不压缩,适合已压缩文件)、ZIP_DEFLATED(默认,平衡推荐)、ZIP_BZIP2(高压缩率)、ZIP_LZMA(最高压缩率)。
3. 核心方法:write()/writestr()添加文件,extract()/extractall()提取文件,namelist()/infolist()/getinfo()查询归档内容。
4. ZipInfo提供完整的文件元数据:file_size、compress_size、date_time、CRC等属性,可用于完整性验证和分析。
5. 密码保护使用ZIP 2.0传统加密,强度有限,敏感数据建议使用更安全的加密方案。
6. 支持从内存中的BytesIO对象直接操作ZIP数据,适合网络传输和临场处理场景。
7. Zlib模块的CRC32校验可用于手动验证ZIP文件的完整性,确保数据在传输或存储过程中未损坏。
7.6 进一步思考
zipfile模块虽然功能全面,但在处理超大归档(数GB级别)时,建议采用分块读取方式以节省内存。对于需要跨平台兼容的场景,注意路径分隔符的统一(统一使用'/')。在安全方面,提取不可信的ZIP文件时应警惕Zip Slip路径遍历攻击,Python 3.8+已经内置了对此类攻击的防护。如果项目中需要创建分卷ZIP(split ZIP),zipfile标准模块暂不支持,可以考虑使用第三方库。
掌握zipfile模块后,建议进一步学习Python的tarfile模块(对应tar.gz/bz2/xz格式)和shutil模块(提供make_archive和unpack_archive等高级封装),形成完整的文件归档处理能力。