2
1

Ruby: AWS S3 のある場所のファイルを別のバケットにまとめてコピーする方法

Last updated at Posted at 2020-01-01

Ruby を使って AWS S3 のある場所のファイルを別のバケットにまとめてコピーする方法を紹介します。

環境

Rails の中のタスクのひとつとして作成しましたが Rails なくても実装可能です。

  • Rails 6.0.2.1
  • Ruby 2.6.5
  • Gem aws-sdk-s3 1.60.1

やりたかったこと

S3 の bucket/folder にあるファイルたちを全て another_bucket/folder 内 に構造を保った状態でコピーしたかった。

プログラム

コピーに使用したプログラムを紹介します。

Rails の中のタスクのひとつとして作成しました。 bundle add aws-sdk-s3 を実行して AWS の SDK を追加しておきます。

# S3 SDK reference: https://docs.aws.amazon.com/sdk-for-ruby/v3/api/Aws/S3/Client.html
require 'uri'

namespace :s3 do
  namespace :something do
    task copy: :environment do
      s3_client = build_s3_client
      copy_bucket_to_bucket(s3_client, 'bucket', 'another_bucket', 'folder/')
    end
  end

  def build_s3_client
    Aws::S3::Client.new(
      access_key_id: Rails.configuration.x.s3_user.access_key,
      secret_access_key: Rails.configuration.x.s3_user.access_secret,
      region: 'ap-northeast-1'
    )
  end

  # @param [Aws::S3::Client] s3_client
  # @param [String] source_bucket
  # @param [String] destination_bucket
  # @param, [String] prefix
  def copy_bucket_to_bucket(s3_client, source_bucket, destination_bucket, prefix = '')
    s3_client.list_objects(bucket: source_bucket, prefix: prefix).to_h[:contents].each do |object|
      puts "copy #{object[:key]}"
      option = {
        #acl: 'public-read',
        bucket: destination_bucket,
        copy_source: URI::encode("/#{source_bucket}/" << object[:key]),
        key: object[:key],
      }
      s3_client.copy_object(option)
    end
  end
end

copy_bucket_to_bucket が実際に S3 内 でのコピーを行うメソッドです。

注意

Bucket のデフォルトの設定では Access Denied というエラーが出るので acl: 'public-read', をコメントアウトしています。
下のように Bucket の設定で 2つのチェックボックスをオフにすると acl: 'public-read' が使えるようになります。

image.png


別アカウントのS3にコピーする場合

require 'uri'
require 'aws-sdk-s3'

def build_source_s3_client
  Aws::S3::Client.new(
    access_key_id:     'SOURCE_ACCESS_KEY',
    secret_access_key: 'SOURCE_ACCESS_SECRET',
    region:            'ap-northeast-1'
  )
end

def build_destination_s3_client
  Aws::S3::Client.new(
    access_key_id:     'DESTINATION_ACCESS_KEY',
    secret_access_key: 'DESTINATION_ACCESS_SECRET',
    region:            'ap-northeast-1'
  )
end

# @param [Aws::S3::Client] source_s3_client
# @param [String] source_bucket
# @param [Aws::S3::Client] destination_s3_client
# @param [String] destination_bucket
# @param [String] prefix
def copy_bucket_to_bucket(
  source_s3_client, source_bucket, destination_s3_client, destination_bucket, prefix = '')

  source_s3_client.list_objects(bucket: source_bucket, prefix: prefix).to_h[:contents].each do |object|
    option = {
      bucket: destination_bucket,
      copy_source: URI::encode("/#{source_bucket}/" << object[:key]),
      key: object[:key],
    }

    # if already exists, skip
    contents = destination_s3_client.list_objects(bucket: destination_bucket, prefix: object[:key]).to_h[:contents]
    if contents != nil && contents.size > 0
      puts "skip #{object[:key]}"
      sleep 0.1
      next
    end

    puts "copy #{object[:key]}"
    source_s3_client.get_object(bucket: source_bucket, key: object[:key]) do |chunk|
      destination_s3_client.put_object(
        bucket: destination_bucket, key: object[:key], body: chunk)
    end
  end
end

source_s3_client      = build_source_s3_client
destination_s3_client = build_destination_s3_client
copy_bucket_to_bucket(
  source_s3_client,      'source-bucket',
  destination_s3_client, 'destination-bucket', '')

使用した環境

  • ruby 2.5.3p105
  • Gem aws-sdk-s3 (1.145.0)
2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1