在内存中创建的Python ZipFile未按预期压缩



我正在尝试使用Python在内存中创建一个ZipFile对象,并将同样在内存中生成的单个文件写入ZipFile对象中,然后将该文件上传到谷歌云存储。

我的文件实际上并没有被压缩。知道我可能做错了什么吗?

我意识到可能有一种更理想的方法可以将行数据输入到文件对象中,但除此之外,我真的只是想弄清楚为什么生成的zip文件根本没有经过压缩。

更新:代码示例现在排除了与谷歌云服务(GCS等(的任何交互,而只是将文件写入磁盘。

似乎当我先将文件写入磁盘,然后创建ZipFile时,结果会按预期压缩,但当我将StringIO内容直接从内存添加到ZipFile对象时,内容不会被压缩。

import random, io, argparse, os, string
from zipfile import ZipFile, ZipInfo, ZIP_DEFLATED
parser = argparse.ArgumentParser()
parser.add_argument("--row_limit", default=1000)
parser.add_argument("--file_name", default='file.txt', type=str)
parser.add_argument("--archive_name", default='file.zip', type=str)
parser.add_argument("--snapshot_millis", default=0, type=int)
args = parser.parse_args()
# imagine this has lots and lots of data in it, coming from a database query result
rows = [{
'seq_no': ''.join(random.choices(string.ascii_uppercase + string.digits, k=args.row_limit)),
'csv': ''.join(random.choices(string.ascii_uppercase + string.digits, k=args.row_limit))
}] * args.row_limit
archive = io.BytesIO()
# create zip archive in memory
with ZipFile(archive, 'w', compression=ZIP_DEFLATED, compresslevel=9) as zip_archive:
count = 0
file_contents = io.StringIO()
for row in rows:
if count > args.row_limit:
break
count += 1
file_contents.write(f"{row['seq_no']},{row['csv']}n")
# write file to zip archive in memory
zip_file = ZipInfo(args.file_name)
zip_archive.writestr(zip_file, file_contents.getvalue())
# also write file to disk
with open(args.file_name, mode='w') as f:
print(file_contents.getvalue(), file=f)
print(f"StringIO Size: {file_contents.tell()}")
print(f"Text File Size On Disk: {os.path.getsize(args.file_name)}")
archive.seek(0)
with open(args.archive_name, 'wb') as outfile:
outfile.write(archive.getbuffer())
print(f"Zip File Created from File In Memory: {os.path.getsize(args.archive_name)}")
ZipFile(args.archive_name, mode='w', compression=ZIP_DEFLATED, compresslevel=9).write(args.file_name)
print(f"Zip File Created from File On Disk: {os.path.getsize(args.archive_name)}")

问题就在这里:

zip_file = ZipInfo(args.file_name)
zip_archive.writestr(zip_file, file_contents.getvalue())

来自ZipFile.writestr文档:

当传递ZipInfo实例作为zinfo_or_arcname参数时使用的压缩方法将是compress_type中指定的方法给定ZipInfo实例的成员。默认情况下,ZipInfo构造函数将此成员设置为ZIP_STORED[即未压缩]

纠正此问题的最简单方法不是使用完整的ZipInfo,而是仅使用文件名。这也会将当前日期/时间设置为档案中文件的创建时间(ZipInfo默认为1980年(:

# zip_file = ZipInfo(args.file_name)
zip_archive.writestr(args.file_name, file_contents.getvalue())

最新更新