zipfile模块 — ZIP归档处理

Python标准库精讲专题 · 压缩与归档篇 · 掌握ZIP文件处理

专题: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_DEFLATEDDEFLATE算法中等较快全版本
ZIP_BZIP2bzip2算法较高中等Python 3.3+
ZIP_LZMALZMA算法最高较慢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 核心属性

属性类型说明
filenamestr归档中的文件名
file_sizeint原始文件大小(字节)
compress_sizeint压缩后的大小(字节)
compress_typeint压缩方法对应的整数常量
date_timetuple文件修改时间 (年,月,日,时,分,秒)
CRCint文件的CRC32校验和
commentbytes单个文件的注释
extrabytes扩展字段数据
extract_versionint解压所需的最低版本
flag_bitsintZIP标志位
volumeint分卷编号
internal_attrint内部属性
external_attrint外部属性(含权限位)
header_offsetint文件头偏移量
file_offsetint文件数据偏移量

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传统加密算法,存在以下局限性:

安全建议:对于需要真正安全加密的场景,建议使用专门的加密库(如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等高级封装),形成完整的文件归档处理能力。