Python zipfile 模块压缩与解压缩通常是对物理磁盘文件进行操作,比如参照官方的例子,生成压缩文件的代码是
with zipfile.ZipFile('spam.zip', 'w') as myzip:
myzip.write('eggs.txt')
myzip.write('beef.txt')
这样就生成了一个包含两个文件的压缩包 spam.zip, 相当于命令 zip spam.zip egges.txt beef.txt
的效果。用 unzip -l spam.zip
命令就能看到其中的两个文件。相应的解压缩的代码如下
with zipfile.ZipFile('spam.zip', 'r') as myzip:
print(myzip.filelist()) # 可获得压缩包中的文件列表信息
myzip.extractall()
同样是把压缩包 spam.zip 解压缩文件到当前目录中,相当于命令 unzip spam.zip
的效果。
前面顺便也是熟悉一下 zipfile 模块的常见用法,但有时候我们可能从数据库中,从网络上收到的是字节数据,希望直接处理字节的压缩解压缩,而不借助于中间的磁盘文件,因为通过磁盘文件来处理必须进行善后处理以及可能的资源的竞争,在内存宽裕的情况下效率也是个问题。
在 Java 中处理内存数据经常用到的是 ByteArrayInputStream
和 ByteArrayOutputStream
,而在 Python 中承担相同角色的就是 io.BytesIO()
。下面来看如何使用它在内存进行双向操作
通过内存生成压缩文件字节内容
本例放到压缩包中的内容是字节,生成的压缩包也是字节表现形式
1 2 3 4 5 6 7 8 9 10 11 12 13 |
import zipfile import io file = io.BytesIO() with zipfile.ZipFile(file, 'w', zipfile.ZIP_DEFLATED) as myzip: myzip.writestr('eggs.txt', 'Here are eggs') myzip.writestr('beef.txt', b'I am beef') myzip.writestr('veggs/pickle.txt', 'pickles') zip_data = file.getvalue() with open('spam.zip', 'wb') as zipfile: zipfile.write(zip_data) |
只需调用 myzip.writestr(filename, original_content) 方法就行,数据可以是字符串或 bytes,filename 部分还可以有目录结构
不指定 zipfile.ZIP_DEFLATED 的话不会对数据进行压缩,只是存储。
上面产生的 zip_data
就是压缩包的字节内容,我们把 zip_data 字节数据保存到 spam.zip
压缩文件,然后用 unzip -l spam.zip
命令查看
1 2 3 4 5 6 7 8 9 |
$ unzip -l spam.zip Archive: spam.zip Length Date Time Name --------- ---------- ----- ---- 13 10-13-2021 22:17 eggs.txt 9 10-13-2021 22:17 beef.txt 7 10-13-2021 22:17 veggs/pickle.txt --------- ------- 29 3 files |
如果解压一下,会看到目录结构与我们设想的一致,三个文件 eggs.txt
, beef.txt
和 veggs/picle.txt
中的内容分别是字符符 Here are eggs
, I am beef
, 和 pickles
。
解压缩 zip 字节内容为字节数据
现在我们直接使用上一步产生的 spam.zip 文件内容,首先假定输入为字节数据,然后窥探其中每一个条目的文件信息与内容
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
import zipfile import io import os def read_zipfiles(path, folder=''): for member in path.iterdir(): filename = os.path.join(folder, member.name) if member.is_file(): print(filename, ':', member.read_text()) # member.read_bytes() else: read_zipfiles(member, filename) with open('spam.zip', 'rb') as myzip: zip_data = myzip.read() with zipfile.ZipFile(io.BytesIO(zip_data)) as zip_file: read_zipfiles(zipfile.Path(zip_file)) |
这里的代码使用到了递归一直罗列压缩包中的条目,压缩包中文件的内容可用 read_text()
和 read_bytes()
分别读出为文本或字节。这是此代码执行后的输出
$ python tt.py
eggs.txt : Here are eggs
beef.txt : I am beef
veggs/pickle.txt : pickles
如果我们明确的知道(比如约定了)压缩包中不会有目录层次,则可不用递归来处理。
链接: