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

55

我需要使用Python Boto API将文件从一个存储桶移动到另一个存储桶。(我需要将文件从第一个存储桶中“剪切”并“粘贴”到第二个存储桶中)。

最佳的方法是什么?

**注意:如果我有两个不同的ACCESS KEYS和SECRET KEYS,这是否重要?

13个回答

53

如果您正在使用较新版本的 boto3,则这很简单。

import boto3
s3 = boto3.resource('s3')
copy_source = {
    'Bucket': 'mybucket',
    'Key': 'mykey'
}
s3.meta.client.copy(copy_source, 'otherbucket', 'otherkey')

(文档)


你不需要从资源对象的元数据中提取客户端。我发现直接通过client.copy_object(或client.copy)使用与建议答案中相同的参数,似乎更加一致(我在元数据中得到了很多404)。https://github.com/boto/boto3/issues/1715 - Ernesto
当你的源和目标是两个具有不同访问密钥的存储桶时,这将如何工作? - David Maddox
37
移动和复制并不是同一件事。 - Chris Ivan
@ChrisIvan 你可以在之前的位置将其删除。如果您对语义有疑问,请提供替代方案。 - David Arenburg
4
@DavidArenburg 需要注意这个区别,因为你的回答读者可能没有意识到,并且问题是关于如何移动的。 - Chris Ivan
1
是的,这是一个重要的区别。我相信每个人都同意答案已经达到了90%的准确度,并且是100%有帮助的。谢谢@DavidArenburg。 - jpmorris

41

我认为 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()

参见:使用s3cmd是否可以将一个S3存储桶中的所有文件复制到另一个存储桶中?


1
我的问题是如何复制文件...? - Gal
8
这可能是最佳的方法。请注意,如果您启用了版本控制,原始存储桶中将留下一些残留数据。另外,您可能希望在复制操作中添加try:expect语句,以便在复制完成之前不会删除任何内容。您还可以复制并跟踪副本,然后浏览目标存储桶并进行key.lookup(),确保其存在,如果存在,则只能执行orig.delete()。 - cgseller
Gal:键是对象,而对象包含内容。通过移动键,您实际上正在移动“文件”。就像在计算机上复制文件时移动文件指针一样,这是相同的方法论。 - cgseller
3
语法似乎不正确,应为dst.copy_key(k.key.name, src.name, k.key.name),因为您需要指定存储桶和对象的名称(而不是它们的对象)- 让我困惑了一段时间 :) - Marty
@marty:谢谢,我认为你说得对,已经相应地更新了答案。 - Freek Wiekmeijer

12

如果您有两个不同的存储桶并且拥有不同的访问凭据,请相应地将凭据存储在“~/.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或存储桶策略中,这两个存储桶无需相互访问。


3
old_obj.get()['Body'].read() 在上传到目标存储桶之前会创建一个本地副本。有没有一种直接从源存储桶复制到目标存储桶的高效方法? - 333

11

相比于逐个复制并删除每个密钥的boto库,awscli对我来说完成工作的速度要快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())

1
我如何在这里提供配置而不设置它们在我的环境变量中? - Nitish Agarwal
不知道有没有简单的方法。在运行驱动程序之前,我会从Python中设置环境变量。 - Artem Fedosov
awscli不是基于boto吗? - gtd
Boto和awscli都基于botocore。但是,boto本身不公开类似于aws s3 mv的API。我仍然没有进行适当的实验来证明mv不等同于cp + rm,但是我真诚地希望如此 :) 我认为对我主要的性能提升是由于awscli中的多线程处理,我可能可以通过自己实现它来达到相似的速度。 - Artem Fedosov
虽然我认为 freek 给出的答案更好,但是我想如果你已经为用户设置了 .aws/config 和 .aws/credentials,你可以使用 driver.main('--profile=myprofile s3 mv source_bucket target_bucket --recursive'.split()) - rabinnh
显示剩余2条评论

6

如果您想创建一个已存储在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()

4

使用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.

我喜欢使用bucket.copy。简单易懂。https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/s3.html#S3.Bucket.copy - MattC

3

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

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

0

我这样做是为了在两个S3位置之间移动文件。

它处理以下情况:

  • 如果您想移动具有特定前缀名称的文件
  • 如果您想在同一个存储桶中的两个子文件夹之间移动它们
  • 如果您想在两个存储桶之间移动它们
import boto3
s3 = boto3.resource('s3')

vBucketName = 'xyz-data-store'
#Source and Target Bucket Instantiation
vTargetBkt = s3.Bucket('xyz-data-store')
vSourceBkt = s3.Bucket('xyz-data-store')

#List of File name prefixes you want to move
vSourcePath = ['abc/1/test1_', 'abc/1/test2_'
               ,'abc/1/test3_','abc/1/test4_']
#List of Folder names you want the files to be moved to
vTargetPath = ['abc/1/test1_', 'abc/1/test2_'
               ,'abc/1/test3_','abc/1/test4_']

for (sP, tP) in zip(vSourcePath,vTargetPath) :
    for se_files in vSourceBkt.objects.filter(Prefix = sP, Delimiter = '/'):
        SourceFileName = (se_files.key).split('/')[-1]
        copy_source = {
            'Bucket': vSourceBkt.name,
            'Key': se_files.key
        }
        #print('SourceFileName ' + SourceFileName)
        #print('se_files ' + se_files.key)
        TargetFileName = str("{}{}".format(tP,SourceFileName))
        print('TargetFileName ' + TargetFileName)
        s3.meta.client.copy(copy_source, vBucketName, TargetFileName)
  
        #Delete files in the Source when the code is working

0
在源AWS帐户中,将此策略添加到源S3存储桶中:
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "s3:*"
            ],
            "Resource": [
                "arn:aws:s3:::SOURCE_BUCKET_NAME",
                "arn:aws:s3:::SOURCE_BUCKET_NAME/*"
            ]
        },
        {
            "Effect": "Allow",
            "Action": [
                "s3:*"
            ],
            "Resource": [
                "arn:aws:s3:::DESTINATION_BUCKET_NAME",
                "arn:aws:s3:::DESTINATION_BUCKET_NAME/*"
            ]
        }
    ]
}

使用目标账户的凭据:
boto3_session = boto3.Session(aws_access_key_id=<your access key>,
                              aws_secret_access_key=<your secret_access_key>)
s3_resource = boto3_session.resource('s3')
bucket = s3_resource.Bucket("<source bucket name>")

for obj in bucket.objects.all():
    obj_path = str(obj.key)

    copy_source = {
        'Bucket': "<source bucket name>",
        'Key': obj_path
    }
    s3_resource.meta.client.copy(copy_source, "<destination bucket name>", obj_path)

0
希望这个答案能有所帮助,谢谢 @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())

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