将内存Zip文件对象上传至S3时触发KeyError错误求助
解决内存Zip文件上传S3时的KeyError问题
你遇到的这个KeyError: 'There is no item named 8388608 in the archive'确实挺诡异的,其实背后是两个容易忽略的细节问题,咱们一步步拆解:
错误原因分析
- 传错了对象给upload_fileobj:你直接把
zipfile.ZipFile实例传给了upload_fileobj,但这个方法需要的是支持read()方法的类文件字节流对象(比如BytesIO、本地文件句柄),而不是ZipFile本身。 - 内存Zip未完成收尾+指针位置错误:在写完所有JSON文件到Zip后,你没有关闭ZipFile(这一步会完成Zip文件的目录结构写入等收尾工作),也没有把
BytesIO缓冲区的指针移到起始位置。这就导致S3读取到的是不完整、无效的Zip数据,从而触发了莫名其妙的KeyError。
至于本地保存后再上传能正常工作,是因为保存本地时,系统会自动帮你完成ZipFile的关闭操作,重新打开文件时指针默认在开头,自然能正确读取。
修正后的代码
不需要用tempfile,用BytesIO就能高效完成内存Zip上传,只要补上关键步骤:
import boto3 import zipfile import json import os from io import BytesIO # 别忘了导入BytesIO! session = boto3.session.Session( aws_access_key_id=os.environ.get('AWS_ACCESS_KEY_ID'), aws_secret_access_key=os.environ.get('AWS_SECRET_ACCESS_KEY')) client = session.client('s3') data = {'test1.json': {'a': 1, 'b': 2}, 'test2.json': {'x': 3, 'y': 4}} zip_buffer = BytesIO() # 用with上下文管理器自动关闭ZipFile,避免遗漏收尾操作 with zipfile.ZipFile(zip_buffer, 'w', zipfile.ZIP_DEFLATED) as zf: # Python3里用items()替代Python2的iteritems() for filename, d in data.items(): zf.writestr(filename, json.dumps(d, indent=4)) # 将缓冲区指针移到文件开头,确保upload_fileobj能从起始位置读取 zip_buffer.seek(0) # 传入zip_buffer字节流对象,而不是ZipFile实例 client.upload_fileobj(zip_buffer, os.environ.get('S3_BUCKET'), 'test_zip.zip')
额外优化点
- 加上
zipfile.ZIP_DEFLATED参数可以启用压缩,减少上传的文件大小(如果不需要压缩可以用默认的ZIP_STORED) - 用
with上下文管理器管理ZipFile,能自动处理关闭操作,比手动调用zf.close()更安全,避免因为代码异常导致的资源泄漏
内容的提问来源于stack exchange,提问作者turnip
相关产品推荐
相关产品推荐

