BOTO3复制S3对象时遭遇Access Denied权限拒绝问题
根据你的场景,我给你整理了两种可行的解决方案——分别针对小文件和大文件,完美适配你「源桶仅能访问部分对象、两个桶用独立密钥」的情况:
核心思路
因为你对源桶只有部分对象的读取权限(能下载但无全桶权限),且两个桶使用完全独立的访问密钥对,直接用S3的服务器端复制(copy_object)大概率会触发权限报错(服务器端复制要求目标桶的身份能直接访问源对象,而你的目标密钥没有源桶的权限)。
最稳妥的方式是:通过源客户端拉取你有权访问的对象内容,再通过目标客户端上传到目标桶——相当于把你本地作为中转,但不用落地到磁盘(大文件可以用分段流处理)。
小文件快速复制方案(≤1GB)
如果你的文件不大,直接用一次性读取+上传即可,代码示例(基于boto3):
# 配置你的桶和对象路径 SOURCE_BUCKET = "your-source-bucket-name" SOURCE_KEY = "path/to/your/source-file.txt" TARGET_BUCKET = "your-target-bucket-name" TARGET_KEY = "path/to/save/target-file.txt" # 1. 从源桶读取对象内容 source_response = client.get_object(Bucket=SOURCE_BUCKET, Key=SOURCE_KEY) object_content = source_response["Body"].read() # 2. 上传到目标桶 client2.put_object( Bucket=TARGET_BUCKET, Key=TARGET_KEY, Body=object_content, # 可选:保留源文件的元数据(比如ContentType) ContentType=source_response.get("ContentType") )
大文件分段复制方案(>1GB)
对于大文件,一次性读取会占用过多内存,推荐用分段上传,既节省内存又能断点续传:
from botocore.exceptions import ClientError def copy_large_s3_object( source_client, target_client, source_bucket, source_key, target_bucket, target_key, part_size=10*1024*1024 # 默认10MB分段,可按需调整 ): # 获取源文件大小,判断是否需要分段 source_obj_meta = source_client.head_object(Bucket=source_bucket, Key=source_key) file_size = source_obj_meta["ContentLength"] # 小文件直接走普通上传 if file_size < part_size: resp = source_client.get_object(Bucket=source_bucket, Key=source_key) target_client.put_object( Bucket=target_bucket, Key=target_key, Body=resp["Body"].read(), ContentType=resp.get("ContentType") ) print(f"✅ 完成小文件复制:{source_key} → {target_key}") return # 初始化分段上传 try: upload_init_resp = target_client.create_multipart_upload( Bucket=target_bucket, Key=target_key, ContentType=source_obj_meta.get("ContentType") ) upload_id = upload_init_resp["UploadId"] parts = [] part_number = 1 offset = 0 # 逐段读取并上传 while offset < file_size: current_part_size = min(part_size, file_size - offset) # 读取源桶的指定字节范围 range_header = f"bytes={offset}-{offset + current_part_size - 1}" source_part_resp = source_client.get_object( Bucket=source_bucket, Key=source_key, Range=range_header ) # 上传当前分段 upload_part_resp = target_client.upload_part( Bucket=target_bucket, Key=target_key, PartNumber=part_number, UploadId=upload_id, Body=source_part_resp["Body"].read() ) parts.append({ "PartNumber": part_number, "ETag": upload_part_resp["ETag"] }) offset += current_part_size part_number += 1 # 完成分段上传 target_client.complete_multipart_upload( Bucket=target_bucket, Key=target_key, UploadId=upload_id, MultipartUpload={"Parts": parts} ) print(f"✅ 完成大文件分段复制:{source_key} → {target_key}") except ClientError as e: # 出错时取消分段上传,避免残留分段占用存储 if 'upload_id' in locals(): target_client.abort_multipart_upload( Bucket=target_bucket, Key=target_key, UploadId=upload_id ) print(f"❌ 复制失败:{str(e)}") raise # 调用示例 copy_large_s3_object( source_client=client, target_client=client2, source_bucket="your-source-bucket", source_key="large/dataset.zip", target_bucket="your-target-bucket", target_key="backup/dataset.zip" )
关键注意事项
- 权限问题:服务器端复制(
copy_object)不适用你的场景,因为它要求目标桶的IAM身份必须拥有源对象的s3:GetObject权限,而你两个桶的密钥是独立的,目标身份没有源桶的权限,所以一定会报错。 - 元数据保留:如果需要保留源文件的自定义元数据、缓存策略等,可以从
head_object的结果中提取Metadata参数,在上传时传入。 - 异常处理:分段上传一定要处理异常,避免中途失败后残留的分段占用S3存储(这些残留分段不会自动删除,会产生额外费用)。
- 分段大小调整:如果你的网络带宽较好,可以把分段调大(比如30MB),减少请求次数;带宽一般的话,保持10-20MB即可。
内容的提问来源于stack exchange,提问作者signasl
相关产品推荐
相关产品推荐

