使用 boto3 在两个 AWS S3 存储桶之间移动文件

Posted

技术标签:

【中文标题】使用 boto3 在两个 AWS S3 存储桶之间移动文件【英文标题】:Move files between two AWS S3 buckets using boto3 【发布时间】:2015-07-21 14:31:18 【问题描述】:

我必须使用 Python Boto API 将文件在一个存储桶之间移动到另一个存储桶。 (我需要它从第一个桶中“剪切”文件并将其“粘贴”到第二个桶中)。 最好的方法是什么?

** 注意:如果我有两个不同的 ACCESS KEYS 和 SECRET KEYS,这有关系吗?

【问题讨论】:

【参考方案1】:

我认为 boto S3 文档回答了您的问题。

https://github.com/boto/boto/blob/develop/docs/source/s3_tut.rst

通过 boto 将文件从一个存储桶移动到另一个存储桶实际上是将密钥从源复制到目标,然后从源中删除密钥。

您可以访问存储桶:

import boto

c = boto.connect_s3()
src = c.get_bucket('my_source_bucket')
dst = c.get_bucket('my_destination_bucket')

并迭代键:

for k in src.list():
    # copy stuff to your destination here
    dst.copy_key(k.key.name, src.name, k.key.name)
    # then delete the source key
    k.delete()

另见:Is it possible to copy all files from one S3 bucket to another with s3cmd?

【讨论】:

我的问题是如何复制文件...? 这可能是最好的方法。请记住,如果您启用了版本控制,则原始存储桶中会留下阴影。此外,您可能希望将您的副本包装在 try:expect 中,这样您就不会在获得副本之前将其删除。您还可以复制并跟踪副本,然后通过 dst 存储桶并执行 key.lookup() 并确保它在那里,如果是,那么只有然后才执行 orig.delete()。 Gal:键是对象,对象包含内容。通过移动键,您可以有效地移动“文件”。可以将其想象为当您在计算机上复制文件时在文件系统中移动文件指针,在底层它是相同的方法。 语法似乎不正确,应该是 dst.copy_key(k.key.name, src.name, k.key.name),因为您需要指定存储桶和键名(而不是它们的对象) - 让我难过一阵子 :) @marty:谢谢,我认为你是对的,并相应地更新了答案。【参考方案2】:

桶名必须是字符串而不是桶对象。 以下更改对我有用

for k in src.list():
    dst.copy_key(k.key, src.name, k.key)

【讨论】:

【参考方案3】:

awscli 比 boto 处理和删除每个键的速度快 30 倍。可能是由于 awscli 中的多线程。如果你仍然想从你的 python 脚本运行它而不从它调用 shell 命令,你可以尝试这样的事情:

安装 awscli python 包:

sudo pip install awscli

然后就这么简单:

import os
if os.environ.get('LC_CTYPE', '') == 'UTF-8':
    os.environ['LC_CTYPE'] = 'en_US.UTF-8'

from awscli.clidriver import create_clidriver
driver = create_clidriver()
driver.main('s3 mv source_bucket target_bucket --recursive'.split())

【讨论】:

如何在不设置环境变量的情况下在这里进行配置 不知道一个简单的方法。我会在运行驱动程序之前从 python 设置环境变量。 awscli 不是基于 boto 的吗? boto 和 awscli 基于 botocore。然而,boto 本身并没有公开类似于aws s3 mv 的 API。我仍然没有进行适当的实验来证明mv 不等于cp + rm,但我真的希望如此 :) 我认为对我来说主要的性能提升是由于 awscli 中的多线程,我可能通过自己实现它可以达到相当相似的速度。 虽然我认为 freek 的以下答案更好,但我想如果你为用户设置了 .aws/config 和 .aws/credentials ,你可以使用driver.main('--profile=myprofile s3 mv source_bucket target_bucket --recursive'.split())【参考方案4】:

如果您使用的是 boto3(较新的 boto 版本),这很简单

import boto3
s3 = boto3.resource('s3')
copy_source = 
    'Bucket': 'mybucket',
    'Key': 'mykey'

s3.meta.client.copy(copy_source, 'otherbucket', 'otherkey')

(Docs)

【讨论】:

您不需要从资源对象的元数据中提取客户端。我发现直接通过client.copy_object(或client.copy)采用与建议答案中的参数相同的参数,并且似乎更一致(我在元数据中得到了很多404)@​​987654322@ 当您的源和目标是具有两个不同访问密钥的两个存储桶时,这将如何工作? 移动和复制不是一回事。 @ChrisIvan 之后您可以将其从旧位置删除。如果您对语义有疑问,请提供替代方案。 @DavidArenburg 注意区别很重要,因为您的答案的读者可能不知道,问题询问如何移动。【参考方案5】:

如果你愿意

创建已存储在 Amazon S3 中的对象的副本。

那么copy_object是进入boto3的方式。

我是怎么做的:

import boto3

aws_access_key_id = ""
aws_secret_access_key = ""
bucket_from = ""
bucket_to = ""
s3 = boto3.resource(
    's3',
    aws_access_key_id=aws_access_key_id,
    aws_secret_access_key=aws_secret_access_key
)
src = s3.Bucket(bucket_from)

def move_files():
    for archive in src.objects.all():
        # filters on archive.key might be applied here

        s3.meta.client.copy_object(
            ACL='public-read',
            Bucket=bucket_to,
            CopySource='Bucket': bucket_from, 'Key': archive.key,
            Key=archive.key
        )

move_files()

【讨论】:

【参考方案6】:

如果您有 2 个具有不同访问凭据的不同存储桶。将凭据相应地存储在 ~/.aws 文件夹下的凭据和配置文件中。

您可以使用以下方法从具有不同凭据的一个存储桶中复制对象,然后将该对象保存在具有不同凭据的另一个存储桶中:

import boto3


session_src = boto3.session.Session(profile_name=<source_profile_name>)
source_s3_r = session_src.resource('s3')

session_dest = boto3.session.Session(profile_name=<dest_profile_name>)
dest_s3_r = session_dest.resource('s3')

# create a reference to source image
old_obj = source_s3_r.Object(<source_s3_bucket_name>, <prefix_path> + <key_name>)

# create a reference for destination image
new_obj = dest_s3_r.Object(<dest_s3_bucket_name>, old_obj.key)

# upload the image to destination S3 object
new_obj.put(Body=old_obj.get()['Body'].read())

两个存储桶不需要在 ACL 或存储桶策略中相互具有可访问性。

【讨论】:

old_obj.get()['Body'].read() 在上传到目标存储桶之前创建一个本地副本。有没有直接从 src 复制到 dest 存储桶的有效方法?【参考方案7】:

这是我用来在 s3 存储桶的子目录中移动文件的代码

# =============================================================================
# CODE TO MOVE FILES within subfolders in S3 BUCKET
# =============================================================================

from boto3.session import Session

ACCESS_KEY = 'a_key'
SECRET_KEY = 's_key'
session = Session(aws_access_key_id=ACCESS_KEY,
            aws_secret_access_key=SECRET_KEY)
s3 = session.resource('s3')#creating session of S3 as resource


s3client = session.client('s3')

resp_dw = s3client.list_objects(Bucket='main_bucket', Prefix='sub_folder/', Delimiter="/")

forms2_dw = [x['Key'] for x in resp_dw['Contents'][1:]]#here we got all files list (max limit is 1000 at a time)
reload_no = 0
while len(forms2_dw) != 0 :

    #resp_dw = s3client.list_objects(Bucket='main_bucket', Prefix='sub_folder/', Delimiter="/")
    #with open('dw_bucket.json','w') as f:
    #    resp_dws =str(resp_dw)
       # f.write(json.dumps(resp_dws))
    #forms_dw = [x['Prefix'] for x in resp_dw['CommonPrefixes']] 
    #forms2_dw = [x['Key'] for x in resp_dw['Contents'][1:]]
    #forms2_dw[-1]
    total_files = len(forms2_dw)
    #i=0
    for i in range(total_files):
    #zip_filename='1819.zip'
        foldername = resp_dw['Contents'][1:][i]['LastModified'].strftime('%Y%m%d')#Put your logic here for folder name
        my_bcket   =  'main_bucket'

        my_file_old = resp_dw['Contents'][1:][i]['Key'] #file to be copied path
        zip_filename =my_file_old.split('/')[-1]
        subpath_nw='new_sub_folder/'+foldername+"/"+zip_filename #destination path
        my_file_new = subpath_nw
        # 
        print str(reload_no)+ ':::  copying from====:'+my_file_old+' to :====='+s3_archive_subpath_nw
        #print my_bcket+'/'+my_file_old 

        if zip_filename[-4:] == '.zip':
            s3.Object(my_bcket,my_file_new).copy_from(CopySource=my_bcket+'/'+my_file_old)
            s3.Object(my_bcket,my_file_old).delete()

            print str(i)+' files moved of '+str(total_files)

    resp_dw = s3client.list_objects(Bucket='main_bucket', Prefix='sub-folder/', Delimiter="/")

    forms2_dw = [x['Key'] for x in resp_dw['Contents'][1:]] 
    reload_no +=1 

【讨论】:

【参考方案8】:

希望这个答案会有所帮助,谢谢@agrawalramakant。

import boto3


# object_key = 'posts/0173c352-f9f8-4bf1-a818-c99b4c9b0c18.jpg'
def move_from_s3_to_s3(object_key):
    session_src = boto3.session.Session(aws_access_key_id="",
                                        region_name="ap-south-1",
                                        aws_secret_access_key="")

    source_s3_r = session_src.resource('s3')

    session_dest = boto3.session.Session(aws_access_key_id="",
                                         region_name="ap-south-1",
                                         aws_secret_access_key="")

    dest_s3_r = session_dest.resource('s3')
    # create a reference to source image
    old_obj = source_s3_r.Object('source_bucket_name', object_key)

    # create a reference for destination image
    new_obj = dest_s3_r.Object('dest_bucket_name', object_key)

    # upload the image to destination S3 object
    new_obj.put(Body=old_obj.get()['Body'].read())

【讨论】:

【参考方案9】:

使用 s3fs 库可以轻松完成。

import s3fs

src = 'source_bucket'
dst = 'destination_bucket'

s3 = s3fs.S3FileSystem(anon=False,key='aws_s3_key',secret='aws_s3_secret_key')

for i in s3.ls(src,refresh=True): # loading the file names
    if 'file_name' in i:          # checking the file name
        s3.mv(i,dst)              # moving file to destination

这是文档。 https://s3fs.readthedocs.io/en/latest/

【讨论】:

【参考方案10】:

不同或相同存储桶之间的复制可以通过以下方式在 boto3 中轻松完成:

import boto3
s3 = boto3.resource('s3')
copy_source = 
    'Bucket': 'mybucket',
    'Key': 'mykey'

bucket = s3.Bucket('otherbucket')
bucket.copy(copy_source, 'otherkey')

# This is a managed transfer that will perform a multipart copy in
# multiple threads if necessary.

【讨论】:

以上是关于使用 boto3 在两个 AWS S3 存储桶之间移动文件的主要内容,如果未能解决你的问题,请参考以下文章

S3 AWS 的 IAM 角色和密钥设置使用 boto3 访问两个不同的账户存储桶

使用 boto3 lib 和 AWS Lambda 从 S3 存储桶中的压缩文件中获取数据流

AWS S3在两个存储桶之间复制文件和文件夹

使用 Ansible 创建 AWS S3 存储桶

无法在特定区域中创建s3存储桶

AWS BOTO3 S3 python - 调用 HeadObject 操作时发生错误(404):未找到