【问题标题】:Amazon S3 concatenate small filesAmazon S3 连接小文件
【发布时间】:2020-11-11 21:37:56
【问题描述】:

有没有办法在 Amazon S3 上连接小于 5MB 的小文件。 由于文件小,不能进行多部分上传。

下拉所有这些文件并进行连接并不是一个有效的解决方案。

那么,谁能告诉我一些 API 来做这些?

【问题讨论】:

  • 文件是否已经在 S3 上?如果没有,你不能在上传之前连接(或压缩)吗?

标签: amazon-web-services amazon-s3 concatenation


【解决方案1】:

Amazon S3 不提供连接功能。它主要是一种对象存储服务。

您将需要一些过程来下载对象、组合它们,然后再次上传它们。最有效的方法是并行下载对象,以充分利用可用带宽。但是,这对代码来说更复杂。

我建议在“云中”进行处理,以避免必须通过 Internet 下载对象。在 Amazon EC2 或 AWS Lambda 上执行此操作会更高效且成本更低。

【讨论】:

  • 旧评论,但这并不完全正确。您可以在 S3 上放置一个 5MB 垃圾对象并与它进行连接,其中第 1 部分 = 5MB 垃圾对象,第 2 部分 = 您要连接的文件。对每个片段重复此操作,最后使用范围副本去除 5MB 垃圾。
  • @wwadge 哦!这是险恶和非常酷!使用Upload Part - Copy 从多个文件复制数据,就好像它们是同一个文件的一部分一样。整洁!
  • 对于上述上传部分 - 复制,只要您的文件全部 > 5MB(其中一个可以更小),它就可以工作。您可以将它们视为 MultiPart Upload,然后让 S3 为您连接它们
【解决方案2】:

编辑:没有看到 5MB 的要求。由于此要求,此方法将不起作用。

来自https://ruby.awsblog.com/post/Tx2JE2CXGQGQ6A4/Efficient-Amazon-S3-Object-Concatenation-Using-the-AWS-SDK-for-Ruby

虽然可以通过以下方式将数据下载并重新上传到 S3 一个 EC2 实例,更有效的方法是指示 S3 使用新的 copy_part API 操作制作内部副本 在 1.10.0 版中引入了 Ruby SDK。

代码:

require 'rubygems'
require 'aws-sdk'

s3 = AWS::S3.new()
mybucket = s3.buckets['my-multipart']

# First, let's start the Multipart Upload
obj_aggregate = mybucket.objects['aggregate'].multipart_upload

# Then we will copy into the Multipart Upload all of the objects in a certain S3 directory.
mybucket.objects.with_prefix('parts/').each do |source_object|

  # Skip the directory object
  unless (source_object.key == 'parts/')
    # Note that this section is thread-safe and could greatly benefit from parallel execution.
    obj_aggregate.copy_part(source_object.bucket.name + '/' + source_object.key)
  end

end

obj_completed = obj_aggregate.complete()

# Generate a signed URL to enable a trusted browser to access the new object without authenticating.
puts obj_completed.url_for(:read)

限制(除其他外)

  • 除最后一部分外,最小部分大小为 5 MB。
  • 已完成的分段上传对象的最大大小限制为 5 TB。

【讨论】:

    【解决方案3】:

    根据@wwadge 的评论,我编写了一个 Python 脚本。

    它通过上传一个略大于 5MB 的虚拟对象来绕过 5MB 限制,然后将每个小文件附加到最后一个文件中。最后,它会从合并的文件中删除虚拟部分。

    import boto3
    import os
    
    bucket_name = 'multipart-bucket'
    merged_key = 'merged.json'
    mini_file_0 = 'base_0.json'
    mini_file_1 = 'base_1.json'
    dummy_file = 'dummy_file'
    
    s3_client = boto3.client('s3')
    s3_resource = boto3.resource('s3')
    
    # we need to have a garbage/dummy file with size > 5MB
    # so we create and upload this
    # this key will also be the key of final merged file
    with open(dummy_file, 'wb') as f:
        # slightly > 5MB
        f.seek(1024 * 5200) 
        f.write(b'0')
    
    with open(dummy_file, 'rb') as f:
        s3_client.upload_fileobj(f, bucket_name, merged_key)
    
    os.remove(dummy_file)
    
    
    # get the number of bytes of the garbage/dummy-file
    # needed to strip out these garbage/dummy bytes from the final merged file
    bytes_garbage = s3_resource.Object(bucket_name, merged_key).content_length
    
    # for each small file you want to concat
    # when this loop have finished merged.json will contain 
    # (merged.json + base_0.json + base_2.json)
    for key_mini_file in ['base_0.json','base_1.json']: # include more files if you want
    
        # initiate multipart upload with merged.json object as target
        mpu = s3_client.create_multipart_upload(Bucket=bucket_name, Key=merged_key)
            
        part_responses = []
        # perform multipart copy where merged.json is the first part 
        # and the small file is the second part
        for n, copy_key in enumerate([merged_key, key_mini_file]):
            part_number = n + 1
            copy_response = s3_client.upload_part_copy(
                Bucket=bucket_name,
                CopySource={'Bucket': bucket_name, 'Key': copy_key},
                Key=merged_key,
                PartNumber=part_number,
                UploadId=mpu['UploadId']
            )
    
            part_responses.append(
                {'ETag':copy_response['CopyPartResult']['ETag'], 'PartNumber':part_number}
            )
    
        # complete the multipart upload
        # content of merged will now be merged.json + mini file
        response = s3_client.complete_multipart_upload(
            Bucket=bucket_name,
            Key=merged_key,
            MultipartUpload={'Parts': part_responses},
            UploadId=mpu['UploadId']
        )
    
    # get the number of bytes from the final merged file
    bytes_merged = s3_resource.Object(bucket_name, merged_key).content_length
    
    # initiate a new multipart upload
    mpu = s3_client.create_multipart_upload(Bucket=bucket_name, Key=merged_key)            
    # do a single copy from the merged file specifying byte range where the 
    # dummy/garbage bytes are excluded
    response = s3_client.upload_part_copy(
        Bucket=bucket_name,
        CopySource={'Bucket': bucket_name, 'Key': merged_key},
        Key=merged_key,
        PartNumber=1,
        UploadId=mpu['UploadId'],
        CopySourceRange='bytes={}-{}'.format(bytes_garbage, bytes_merged-1)
    )
    # complete the multipart upload
    # after this step the merged.json will contain (base_0.json + base_2.json)
    response = s3_client.complete_multipart_upload(
        Bucket=bucket_name,
        Key=merged_key,
        MultipartUpload={'Parts': [
           {'ETag':response['CopyPartResult']['ETag'], 'PartNumber':1}
        ]},
        UploadId=mpu['UploadId']
    )
    

    如果您已经有一个 >5MB 的对象还想添加更小的部分,那么请跳过创建虚拟文件和最后一个带有字节范围的副本部分。另外,我不知道这对大量非常小的文件如何执行 - 在这种情况下,最好下载每个文件,在本地合并它们然后上传。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-04-15
      • 2017-09-29
      • 2012-07-16
      • 2017-01-26
      • 2015-10-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多