Python boto3跨S3端点流式拷贝对象 上传0字节问题求解
跨服务商S3端点流式拷贝对象问题修复
问题根因
你写的两段代码上传得到0字节文件的核心原因非常明确:
download_fileobj把源对象数据写入BytesIO对象后,流的读写指针会停在数据末尾位置- 后续调用
upload_fileobj时,会从当前指针位置开始读取数据,从末尾读自然读不到任何内容,最终上传空文件 - 另外你当前的写法本质是把整个对象全量加载到内存,根本没实现流式拷贝,遇到超过内存大小的大文件会直接触发OOM,完全不满足大文件拷贝的要求。
如果只是临时测试小文件,你给的代码加一行指针重置就能跑通:
import io import boto3 data = io.BytesIO() src_client.download_fileobj(args.SRCBUCKET, args.SRCKEY, data) data.seek(0) # 把读写指针移回流的起始位置 dst_client.upload_fileobj(data, args.DSTBUCKET, args.DSTKEY, # 可根据主机内存调整分块大小,比如设为16MB Config=boto3.s3.transfer.TransferConfig(multipart_chunksize=16*1024*1024))
注意:这个写法依然会把整个对象读入内存,仅适合小文件场景,不能用于超大对象拷贝
满足所有约束的流式拷贝实现
这个实现完全匹配你列出的所有要求:
- 固定内存占用(仅占用和分块大小一致的内存,比如16MB),不管对象多大都不会撑爆内存,真正实现流式传输
- 不需要源端和目标端S3有直连网络,所有流量经过运行脚本的中间主机
- 不需要配置跨账号/跨服务商的CopyObject权限,全程用两边各自的访问凭证读写
- 支持拷贝过程中按映射表自定义目标对象名,实现实时重命名
- 不需要把文件全量落到本地磁盘
- 兼容不同服务商的S3协议实现
完整可运行代码如下:
import boto3 from boto3.s3.transfer import TransferConfig # 初始化两端S3客户端,自行替换各服务商的endpoint、密钥配置 src_client = boto3.client( 's3', endpoint_url='源S3端点地址', aws_access_key_id='源端AK', aws_secret_access_key='源端SK' ) dst_client = boto3.client( 's3', endpoint_url='目标S3端点地址', aws_access_key_id='目标端AK', aws_secret_access_key='目标端SK' ) def cross_s3_stream_copy(src_bucket, src_key, dst_bucket, dst_key, chunk_size=16*1024*1024): """ 跨S3端点流式拷贝,固定内存占用,支持超大文件 :param chunk_size: 分块大小,默认16MB,可根据主机内存调整,最小不低于5MB(S3分块上传强制要求) """ # 配置传输参数,禁用多线程避免流读乱序,固定分块大小 transfer_config = TransferConfig( multipart_chunksize=chunk_size, multipart_threshold=chunk_size, use_threads=False, max_concurrency=1 ) # 发起源端GetObject请求,拿到流式响应体(不会一次性加载全量数据) src_resp = src_client.get_object(Bucket=src_bucket, Key=src_key) src_stream = src_resp['Body'] # 直接把源端流式响应体传给目标端upload_fileobj,边读边传 # 不需要额外建BytesIO缓存全量数据,内存里始终只有当前分块的内容 dst_client.upload_fileobj( Fileobj=src_stream, Bucket=dst_bucket, Key=dst_key, Config=transfer_config ) src_stream.close() # 调用示例:接入静态重命名映射表即可实现拷贝时实时重命名 if __name__ == '__main__': # 替换成你自己的静态映射表 key_mapping = { "源对象key1": "重命名后的目标key1", "源对象key2": "重命名后的目标key2" } src_key = "待拷贝的源对象key" dst_key = key_mapping.get(src_key, src_key) # 按映射表匹配目标key cross_s3_stream_copy( src_bucket="源桶名", src_key=src_key, dst_bucket="目标桶名", dst_key=dst_key )
实现注意事项
- 分块大小
chunk_size不要低于5MB,否则S3分块上传会直接报错,建议设为10MB~32MB之间,根据主机剩余内存调整即可 - 必须把传输配置里的
use_threads设为False、max_concurrency设为1,否则boto3默认会开多线程并发读流,而源端返回的HTTP流不支持多线程随机读,会导致拷贝数据错乱 - 如果遇到个别小众S3服务商对分块上传的签名校验逻辑特殊,可以适当调大
multipart_threshold,小文件走普通上传,大文件自动走分块即可 - 如果后续需要加MD5校验、传输进度条能力,直接包装一层
src_stream实现自定义read方法即可,不需要改动核心传输逻辑
兜底方案
如果遇到兼容性极差的S3服务商,流式上传始终异常,再考虑落盘中转方案:用Linux系统临时目录做缓存,拷贝完成后自动删除临时文件即可,不会长期占用磁盘空间。
内容的提问来源于stack exchange,提问作者DougH
相关产品推荐
相关产品推荐

