zip文件压缩解压
使用 python 操作 zip 文件,压缩和解压。
压缩
压缩文件时,可以将压缩文件保存在本地或保存在内存中,将压缩文件保存在内存中时,方便后续使用,比如直接返回给前端,也就不用再执行删除操作。
import zipfile
from pathlib import Path
from io import BytesIO
def zip_to_file():
"""压缩文件保存到文件中"""
zip_file = Path('./test.zip')
with zipfile.ZipFile(zip_file, 'w', zipfile.ZIP_DEFLATED) as zip_obj:
# 将字符串写入文件压缩
zip_obj.writestr('test.txt', 'test file')
# 压缩本地文件
f = Path('./test.json')
if f.exists():
zip_obj.write(f, 'test.json')
def zip_to_mem():
"""压缩文件保存到内存中"""
with BytesIO() as bio, zipfile.ZipFile(bio, 'w', zipfile.ZIP_DEFLATED) as zip_obj: # 使用 BytesIO 接收压缩文件
# 将字符串写入文件压缩
zip_obj.writestr('test.txt', 'test file')
# 压缩本地文件
f = Path('./test.json')
if f.exists():
zip_obj.write(f, 'test.json')
# 保存数据时,需要先手动 close,否则最后写入的文件会缺少一部分数据
zip_obj.close()
# 将内存中的数据保存到文件中方便展示效果,实际中可以直接发送给前端或其他处理
with open('test.zip', 'wb') as fw:
fw.write(bio.getvalue())
if __name__ == '__main__':
zip_to_mem()
问题
当把压缩文件保存在 BytesIO 中时,按照以下方式保存时,使用 zipfile 去解压压缩文件,报错zipfile.BadZipFile: File is not a zip file
,但用其他压缩软件可以正常解压。
with BytesIO() as bio, zipfile.ZipFile(bio, 'w', zipfile.ZIP_DEFLATED) as zip_obj: # 使用 BytesIO 接收压缩文件
# 将字符串写入文件压缩
zip_obj.writestr('test.txt', 'test file')
# 压缩本地文件
f = Path('./test.json')
if f.exists():
zip_obj.write(f, 'test.json')
# 将内存中的数据保存到文件中方便展示效果,实际中可以直接发送给前端或其他处理
with open('test.zip', 'wb') as fw:
fw.write(bio.getvalue())
检查 bio 中数据后,发现在 ZipFile 对象close
方法中还会调用_write_end_record
写入数据,而使用上面的保存方式时,在写入本地文件时,ZipFile 对象还没有调用close
方法,导致最后写入的数据缺少一部分,因此在写入本地文件前,手动调用close
方法。
异常数据:b'PK\x03\x04\x14\x00\x00\x00\x08\x00\x95\x8d[W\xc1kd\xf2\x0b\x00\x00\x00\t\x00\x00\x00\x08\x00\x00\x00test.txt+I-.QH\xcb\xccI\x05\x00'
正常数据:b'PK\x03\x04\x14\x00\x00\x00\x08\x00\x95\x8d[W\xc1kd\xf2\x0b\x00\x00\x00\t\x00\x00\x00\x08\x00\x00\x00test.txt+I-.QH\xcb\xccI\x05\x00PK\x01\x02\x14\x00\x14\x00\x00\x00\x08\x00\x95\x8d[W\xc1kd\xf2\x0b\x00\x00\x00\t\x00\x00\x00\x08\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\x01\x00\x00\x00\x00test.txtPK\x05\x06\x00\x00\x00\x00\x01\x00\x01\x006\x00\x00\x001\x00\x00\x00\x00\x00'
解压
可以读取本地压缩文件或内存中压缩文件,然后解压到本地目录或内存中
import json
import zipfile
from pathlib import Path
def unzip_to_file():
"""压缩到文件中"""
zip_file = Path('./test.zip')
with zipfile.ZipFile(zip_file, 'r') as zip_obj:
# 直接解压在当前目录
zip_obj.extractall()
def unzip_to_mem():
"""解压到内存中"""
zip_file = Path('./test.zip')
with zipfile.ZipFile(zip_file, 'r') as zip_obj:
# 循环压缩包中的所有文件
for file_info in zip_obj.filelist:
# 预防压缩炸弹,限制解压文件大小
if file_info.file_size > 1024 * 1024 * 2:
print(f'{file_info.filename} 文件大小异常')
break
if file_info.filename == 'test.txt':
# 读取文件打印文件数据
print(zip_obj.open(file_info).read())
def unzip_from_to_mem():
"""解压到内存中"""
zip_file = Path('./test.zip')
# 示例为了演示,先使用 open 打开文件,再传入 ZipFile,实际中,数据可能来自于前端上传等
with open(zip_file, 'rb') as f, zipfile.ZipFile(f, 'r') as zip_obj:
# 循环压缩包中的所有文件
for file_info in zip_obj.filelist:
# 预防压缩炸弹,限制解压文件大小
if file_info.file_size > 1024 * 1024 * 2:
print(f'{file_info.filename} 文件大小异常')
continue
if file_info.filename == 'test.json':
# 读取文件打印文件数据
print(json.loads(zip_obj.open(file_info).read()))
if __name__ == '__main__':
unzip_from_to_mem()
PS
- 在读取内存中压缩文件时,示例是使用 open 打开文件,仅作为示例,实际中,应该是前端上传数据或其他方式
- 当使用 flask 接收上传的压缩文件时,如果后面会多次使用该数据,需要先将数据读取出来方便后续使用
bytes_io = BytesIO(request.files.get('file').stream.read())