如何以流式方式解压S3中存储的嵌套tar格式大文件
解决方案
问题根因
你遇到的问题核心有两点:
- 直接读取
d.raw.fileobj绕过了tar库对单个压缩包成员的长度边界校验,会直接消费整个解压后的内层tar流,因此会一次性返回所有嵌套文件内容 d.read()返回为空大多是因为流被提前消费、或是遍历tar成员时操作顺序有误,流式tar模式(r|*)下只能按顺序消费流,不可回溯。
正确实现代码
import boto3 import tarfile s3_client = boto3.client('s3') # 替换为实际的bucket名和文件key bucket = "your-bucket-name" key = "path/to/your/large.tar" resp = s3_client.get_object(Bucket=bucket, Key=key) # 外层tar用流式只读模式,适配S3 StreamingBody不支持seek的特性 with tarfile.open(fileobj=resp['Body'], mode='r|') as outer_tar: for outer_item in outer_tar: # 只处理普通文件,跳过目录、符号链接等特殊类型 if not outer_item.isfile(): continue # 可选:增加文件名后缀判断,只处理tar.gz文件,避免报错 if not outer_item.name.endswith('.tar.gz'): # 如果需要处理外层tar中的普通非压缩文件,可在这里补充对应逻辑 continue # 提取内层tar.gz的流式文件对象 inner_tar_obj = outer_tar.extractfile(outer_item) if inner_tar_obj is None: continue # 打开内层gzip压缩的tar,同样使用流式模式 with tarfile.open(fileobj=inner_tar_obj, mode='r|gz') as inner_tar: for inner_item in inner_tar: if not inner_item.isfile(): continue # 提取内层单个文件的流对象 inner_file = inner_tar.extractfile(inner_item) if inner_file is None: continue # 此处read()拿到的就是当前内层单个文件的完整内容,不会多读其他文件数据 file_content = inner_file.read() # 替换为自身业务处理逻辑即可 print(f"处理文件:{inner_item.name},大小:{len(file_content)}")
注意事项
- 全程采用流式顺序读取逻辑,不会将整个S3文件、或是整个tar文件加载到内存,可支持几十GB级的大文件处理
- 流式tar模式(
r|/r|gz)不支持随机访问,不要调用seek()类方法,也不要打乱成员遍历顺序 - S3返回的
StreamingBody本身就符合流式类文件对象的要求,无需额外做包装转换
内容的提问来源于stack exchange,提问作者Nowandthen98
相关产品推荐
相关产品推荐

