boto3 s3复制对象错误

4
我正在尝试使用Lambda和Boto3将文件从一个存储桶复制到同一存储桶中的另一个前缀,但我一直收到错误消息:无权访问时调用CopyObject操作导致错误(AccessDenied);根据所使用的复制方法,也可能出现调用HeadObject操作时发生错误(Forbidden)的情况。
我认为Lambda函数已有分配的角色,该角色赋予了函数所需的所有权限。
{
"Version": "2012-10-17",
"Statement": [
    {
        "Action": [
            "s3:HeadObject",
            "s3:ListObjects"
        ],
        "Resource": [
            "arn:aws:s3:::bucket-name",
            "arn:aws:s3:::bucket-name/*"
        ],
        "Effect": "Allow"
    },
    {
        "Action": [
            "s3:GetObject",
            "s3:PutObject",
            "s3:DeleteObject"
        ],
        "Resource": [
            "arn:aws:s3:::bucket-name/folderA/folderB/*",
            "arn:aws:s3:::bucket-name/folderC/folderD/*",
            "arn:aws:s3:::bucket-name/folderE/folderF/*"
        ],
        "Effect": "Allow"
    }
]
}

lambda函数是:

#connect to s3
s3 = boto3.resource('s3')

dirs = {
    "folderA/folderB": "folderC/folderD"        
}    

key = urllib.parse.unquote_plus(event['Records'][0]['s3']['object']['key'], encoding='utf-8')
etag = urllib.parse.unquote_plus(event['Records'][0]['s3']['object']['eTag'], encoding='utf-8')    
bucket = event['Records'][0]['s3']['bucket']['name']

filePathName = key.split("/")
sourceDir = filePathName[0] + "/" + filePathName[1]
fileName = filePathName[2]

sourceKey = sourceDir + "/" + fileName
source = {'Bucket': bucket, 'Key': sourceKey}
destination = dirs[sourceDir] + "/" + fileName

##########
# This option comes up with the An error occurred (AccessDenied) when calling the CopyObject operation. Error
###########
s3.Object(bucket, destination).copy_from(CopySource=source)

###########
## This option comes up with the An error occurred (403) when calling the HeadObject operation: Forbidden error
###########
s3.meta.client.copy(source, bucket, destination)

抱歉,我没有理解您的请求。请提供需要翻译的文本。
{
"Version": "2012-10-17",
"Statement": [
    {
        "Action": [
            "s3:*"
        ],
        "Resource": [
            "arn:aws:s3:::bucket-name",
            "arn:aws:s3:::bucket-name/*"
        ],
        "Effect": "Allow"
    }

乍一看起来还不错。您是否暂时已经给Lambda函数s3:* 授权到arn:aws:s3:::*,以验证在完全授权时它能否正常工作?只是为了排除某些傻瓜问题,例如错误的存储桶名称?另外,请记录各种值,例如存储桶和源代码,以进行双重检查。 - jarmod
已经修复了这个问题,但很遗憾那不是原因。 - vanquish
好的,你知道一组权限不足和一组足够的权限,因此最小的权限集位于两者之间。我不知道有任何文档可以帮助这里,所以我建议使用二分法方法。 - jarmod
除了 s3:Put、s3:Get 和 s3:List* 之外,我还能提供什么? - vanquish
例如HeadBucket。AWS记录了S3操作列表(https://docs.aws.amazon.com/IAM/latest/UserGuide/list_s3.html)。 - jarmod
显示剩余4条评论
3个回答

9
我遇到了类似的问题。解决方法是:在CopySource=source中,source必须是从存储桶根目录到实际文件的完整路径,而不是存储桶名称和键的字典。因此,我认为您的代码可能需要改为:

s3.Object(bucket, destination).copy_from(CopySource=bucket + sourceDir)


请问这个是如何工作的?我没有看到你在哪里指定目标凭据和源凭据。 - saadi
这是对原帖作者代码的修改。因此,您可以在原帖中看到这些变量的定义。 - bluppfisk
1
存储桶名称和键的字典实际上是指定CopySource的推荐方式。 参考:https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/s3.html#S3.Object.copy_from - Anu

1
这个问题很老了,但对某些人可能会有帮助:
copy_source = {
    'Bucket': 'my_perfect_bucket',
    'Key': 'media/old_path/glory.jpg'
}

s3.Object('my_perfect_bucket','media/new_path/glory.jpg').copy_from(CopySource=copy_source)

0
我注意到这里的许多其他解决方案在处理大型存储桶或在没有aws-cli可用的环境中并不特别“即插即用”。
这个解决方案不依赖于aws-cli,并使用boto3分页器来处理大型存储桶。
import boto3

def recursive_s3_cp(source_bucket, source_bucket_path, dest_bucket, dest_bucket_path):
    s3_client = boto3.client("s3")
    paginator = s3_client.get_paginator('list_objects_v2')
    response_iterator = paginator.paginate(
            Bucket=source_bucket,
            Prefix=source_bucket_path,
            Delimiter="/"
            )
    result = response_iterator.build_full_result()
    # Skip first item as it's a reference to the prefix directory
    for bucket_object in result['Contents'][1:]:
        new_path = bucket_object['Key'].replace(source_bucket_path, dest_bucket_path)
        response = s3_client.copy_object(
                # Tons of other parameters you can pass here if you need them
                Bucket=dest_bucket,
                CopySource={
                    'Bucket': source_bucket,
                    'Key': bucket_object['Key']
                    },
                Key=new_path,
                )
        if not response['ResponseMetadata']['HTTPStatusCode'] == 200:
            raise Exception('s3 copy failed with response: ', response)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接