使用s3cmd可以将一个S3存储桶中的所有文件复制到另一个存储桶吗?

81

我非常喜欢s3cmd,但有一个问题:如何将一个S3存储桶中的所有文件复制到另一个存储桶?这是否可能?

编辑:我已经找到了一种使用Python和boto复制存储桶之间文件的方法:

from boto.s3.connection import S3Connection

def copyBucket(srcBucketName, dstBucketName, maxKeys = 100):
  conn = S3Connection(awsAccessKey, awsSecretKey)

  srcBucket = conn.get_bucket(srcBucketName);
  dstBucket = conn.get_bucket(dstBucketName);

  resultMarker = ''
  while True:
    keys = srcBucket.get_all_keys(max_keys = maxKeys, marker = resultMarker)

    for k in keys:
      print 'Copying ' + k.key + ' from ' + srcBucketName + ' to ' + dstBucketName

      t0 = time.clock()
      dstBucket.copy_key(k.key, srcBucketName, k.key)
      print time.clock() - t0, ' seconds'

    if len(keys) < maxKeys:
      print 'Done'
      break

    resultMarker = keys[maxKeys - 1].key

同步几乎和复制一样简单。键值对中有ETag、大小(size)和上次修改时间(last-modified)的字段可用。

也许这对其他人也有帮助。


3
嗨,您能把您的修改转成答案并接受吗?这是一个非常有用的提示! - Hamish
1
你为什么使用“get_all_keys”而不是“list”? - Bill Rosmus
11个回答

101

s3cmd sync s3://from/this/bucket/ s3://to/this/bucket/

可用选项,请使用: $s3cmd --help


1
很棒的建议。喜欢那个 s3cmd。尾随斜杠可能很重要,所以 s3cmd sync s3://sample_bucket/ s3://staging_bucket/ 对我很有效。 - Charles Forcey
13
您也可以使用 AWS CLI 来完成此操作。使用命令 aws s3 sync s3://from/ s3://to/ 即可。 - Bobo
2
如果每个存储桶具有不同的访问密钥ID和秘钥(不同的AWS账户),该怎么办? - brainstorm
一个快速的问题,这个程序是否直接在存储桶之间同步文件而不先将它们复制到本地?我认为是这样,但还是想再确认一下... - Brett
是的,我认为这是直接复制,没有涉及本地存储。亚马逊确实有一个用于S3的复制API,s3cmd可能会使用它: http://docs.aws.amazon.com/AmazonS3/latest/API/RESTObjectCOPY.html - amit_saxena
显示剩余3条评论

50

是的。但是没有请求者支付参数等。 - Artem

30

截至我撰写本文时,得票最高的答案是:

s3cmd sync s3://from/this/bucket s3://to/this/bucket

这是一个有用的答案。但有时同步不是你所需要的(它会删除文件等)。我花了很长时间才找到这个非脚本替代方案,可以简单地在存储桶之间复制多个文件。(好吧,在下面展示的情况下,它不是在存储桶之间。它是在“伪文件夹”之间工作,但在存储桶之间同样有效。)

# Slightly verbose, slightly unintuitive, very useful:
s3cmd cp --recursive --exclude=* --include=file_prefix* s3://semarchy-inc/source1/ s3://semarchy-inc/target/

上述命令的解释:

  • --recursive
    在我的脑海中,我并不需要递归。我只想要多个文件。但是在这种情况下,recursive告诉s3cmd cp处理多个文件。很好。
  • --exclude
    这是一个奇怪的思考问题的方式。先递归选择所有文件,然后排除所有文件。等等,什么?
  • --include
    现在我们来谈谈实质性的内容。指定您想要包括的文件前缀(或后缀或任何其他模式)。
    s3://sourceBucket/ s3://targetBucket/
    这部分很直观。虽然从技术上讲,它似乎违反了s3cmd帮助文档中的示例,该示例表明必须指定源对象:
    s3cmd cp s3://BUCKET1/OBJECT1 s3://BUCKET2[/OBJECT2]

为了让这个好答案变得更好,请将您in-depth blog post的“Enlightenment”部分复制到此处的答案中。做得很好! - Iain Samuel McLean Elder
你不能用以下命令达到同样的效果吗:s3cmd sync --max-delete=0 s3://from s3://to - schmijos
嗯...我从未找到过那个选项。所以我无法确认它是否有效。但我不明白为什么它不会有效。事实上,现在我看到了--no-delete-removed,这似乎更加切题。 - mdahlman

10
您也可以使用Web界面完成此操作:
  1. 进入Web界面的源存储桶。
  2. 标记您想要复制的文件(使用Shift和鼠标点击可选择多个文件)。
  3. 按下“操作”->“复制”。
  4. 进入目标存储桶。
  5. 按下“操作”->“粘贴”。
就这样。

8

3

实际上是可以做到的。这对我有效:

import boto


AWS_ACCESS_KEY = 'Your access key'
AWS_SECRET_KEY = 'Your secret key'

conn = boto.s3.connection.S3Connection(AWS_ACCESS_KEY, AWS_SECRET_KEY)
bucket = boto.s3.bucket.Bucket(conn, SRC_BUCKET_NAME)

for item in bucket:
    # Note: here you can put also a path inside the DEST_BUCKET_NAME,
    # if you want your item to be stored inside a folder, like this:
    # bucket.copy(DEST_BUCKET_NAME, '%s/%s' % (folder_name, item.key))
    bucket.copy(DEST_BUCKET_NAME, item.key)

复制方法适用于boto.s3.key对象,请参阅此处。但这是一种直接复制/移动文件而不必担心“子文件夹”细节的好方法。 - GeoSharp

2

mdahlman的代码对我不起作用,但这个命令将bucket1中的所有文件复制到bucket2中的一个新文件夹(该命令还创建此新文件夹)。

cp --recursive --include=file_prefix* s3://bucket1/ s3://bucket2/new_folder_name/

2
这个命令不起作用,cp 前面应该加什么?是 aw3、s3cmd 还是其他什么? - in_user

2

谢谢 - 我使用的是稍作修改的版本,其中我只复制不存在或大小不同的文件,并在目标位置检查源中是否存在该密钥。我发现这样可以更快地准备测试环境:

def botoSyncPath(path):
    """
       Sync keys in specified path from source bucket to target bucket.
    """
    try:
        conn = S3Connection(AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY)
        srcBucket = conn.get_bucket(AWS_SRC_BUCKET)
        destBucket = conn.get_bucket(AWS_DEST_BUCKET)
        for key in srcBucket.list(path):
            destKey = destBucket.get_key(key.name)
            if not destKey or destKey.size != key.size:
                key.copy(AWS_DEST_BUCKET, key.name)

        for key in destBucket.list(path):
            srcKey = srcBucket.get_key(key.name)
            if not srcKey:
                key.delete()
    except:
        return False
    return True

2
我写了一个备份S3存储桶的脚本:https://github.com/roseperrone/aws-backup-rake-task
#!/usr/bin/env python
from boto.s3.connection import S3Connection
import re
import datetime
import sys
import time

def main():
    s3_ID = sys.argv[1]
    s3_key = sys.argv[2]
    src_bucket_name = sys.argv[3]
    num_backup_buckets = sys.argv[4]
    connection = S3Connection(s3_ID, s3_key)
    delete_oldest_backup_buckets(connection, num_backup_buckets)
    backup(connection, src_bucket_name)

def delete_oldest_backup_buckets(connection, num_backup_buckets):
    """Deletes the oldest backup buckets such that only the newest NUM_BACKUP_BUCKETS - 1 buckets remain."""
    buckets = connection.get_all_buckets() # returns a list of bucket objects
    num_buckets = len(buckets)

    backup_bucket_names = []
    for bucket in buckets:
        if (re.search('backup-' + r'\d{4}-\d{2}-\d{2}' , bucket.name)):
            backup_bucket_names.append(bucket.name)

    backup_bucket_names.sort(key=lambda x: datetime.datetime.strptime(x[len('backup-'):17], '%Y-%m-%d').date())

    # The buckets are sorted latest to earliest, so we want to keep the last NUM_BACKUP_BUCKETS - 1
    delete = len(backup_bucket_names) - (int(num_backup_buckets) - 1)
    if delete <= 0:
        return

    for i in range(0, delete):
        print 'Deleting the backup bucket, ' + backup_bucket_names[i]
        connection.delete_bucket(backup_bucket_names[i])

def backup(connection, src_bucket_name):
    now = datetime.datetime.now()
    # the month and day must be zero-filled
    new_backup_bucket_name = 'backup-' + str('%02d' % now.year) + '-' + str('%02d' % now.month) + '-' + str(now.day);
    print "Creating new bucket " + new_backup_bucket_name
    new_backup_bucket = connection.create_bucket(new_backup_bucket_name)
    copy_bucket(src_bucket_name, new_backup_bucket_name, connection)


def copy_bucket(src_bucket_name, dst_bucket_name, connection, maximum_keys = 100):
    src_bucket = connection.get_bucket(src_bucket_name);
    dst_bucket = connection.get_bucket(dst_bucket_name);

    result_marker = ''
    while True:
        keys = src_bucket.get_all_keys(max_keys = maximum_keys, marker = result_marker)

        for k in keys:
            print 'Copying ' + k.key + ' from ' + src_bucket_name + ' to ' + dst_bucket_name

            t0 = time.clock()
            dst_bucket.copy_key(k.key, src_bucket_name, k.key)
            print time.clock() - t0, ' seconds'

        if len(keys) < maximum_keys:
            print 'Done backing up.'
            break

        result_marker = keys[maximum_keys - 1].key

if  __name__ =='__main__':main()

我在 Rails 应用程序中使用 rake 任务:

desc "Back up a file onto S3"
task :backup do
     S3ID = "*****"
     S3KEY = "*****"
     SRCBUCKET = "primary-mzgd"
     NUM_BACKUP_BUCKETS = 2

     Dir.chdir("#{Rails.root}/lib/tasks")
     system "./do_backup.py #{S3ID} #{S3KEY} #{SRCBUCKET} #{NUM_BACKUP_BUCKETS}"
end

1

s3cmd不支持仅使用前缀或通配符进行复制,但您可以使用“s3cmd ls sourceBucket”脚本行为,并使用awk提取对象名称。然后使用“s3cmd cp sourceBucket/name destBucket”将列表中的每个对象名称复制到目标桶。

我在Windows的DOS命令行中使用这些批处理文件:

s3list.bat

s3cmd ls %1 | gawk "/s3/{ print \"\\"\"\"substr($0,index($0,\"s3://\"))\"\\"\"\"; }"

s3copy.bat

@for /F "delims=" %%s in ('s3list %1') do @s3cmd cp %%s %2

请注意,这种方法非常慢(就像其他一次只处理一个对象的解决方案一样),但如果要复制的项目不太多,则可以使用该方法。 - Joshua Richardson
这个答案长时间以来一直欺骗着我...但事实上,如果您使用正确(有些不直观)的选项集,s3cmd可以使用通配符进行复制。我发布了一个详细的答案。 - mdahlman

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