LoginSignup
6
7

More than 3 years have passed since last update.

AWS SDK for RubyでS3のオブジェクトを扱う (まとめて削除)。

Last updated at Posted at 2020-03-01

これはなに?

AWS S3のオブジェクトをAWS SDK for Rubyで扱う時のメモです。今回は削除に関してのメモになります。
すでにたくさんの記事にわたしもお世話になりましたので、網羅的に参考にしたい場合はぜひ下記の記事もどうぞ!(ありがとうございます!)

オブジェクトの削除をしたい

基本は オブジェクトを特定 が必要になります。
また、特定したオブジェクトに対して削除APIを呼び出す形になります。

こちらに関しては、ここまで。以下、「たくさんあるとき」について記載してみます。

オブジェクトの一括削除をしたい

こちらも、基本は オブジェクトを特定 が必要になります。

  • 特定したオブジェクトに対して削除APIを個数分呼び出す
  • 特定したオブジェクトを一括で削除する (一括削除のAPIを呼び出してまとめて削除する形になります)

丁寧に一回一回削除APIを呼ぶと、時間がかかりますね!(もちろん確実ではありますが)

ここでは、一括削除のAPI を呼び出す方法を取ってみます。
利用するのは、AWS SDK for Ruby (version 3) です。

削除の流れ (オブジェクト特定 -> 一括削除)

以下のようになります。

  • オブジェクトのkeyはアクセス可能なオブジェクトのURLからはわからないので、まずオブジェクトを取得しkeyを取り出します
  • 複数ある場合は、Aws::S3::Bucket#objects や Aws::S3::Client#list_objects_v2 や を使います(後述)
    • Aws::S3::Client#list_objects_v2は一回につき 1000件まで の制約があります!
  • オブジェクト、もしくはオブジェクトのkeyを配列に入れて、一括削除用のメソッドに引き渡します
    • これも、利用できるメソッドが Aws::S3::Bucketのインスタンス / Aws::S3::Clientのインスタンスの2つがあります

Aws::S3::Bucket#objects を使う場合

  • Aws::S3::Bucket#objects だと1000件単位かどうかを気にせず、一括で取れる ので、こちらを利用がいいかもしれません

# ここではまず Aws::S3::Resource を使います
s3_resources = Aws::S3::Resource.new(
  access_key_id: アクセスキーID,
  secret_access_key: シークレットアクセスキー,
  region: リージョン
)

bucket_name = '作業したいバケット名'

# Aws::S3::Bucketのインスタンスを取得します
bucket = s3_resources.bucket(bucket_name)

# 特定のプレフィックス以下を対象に抽出したいときは、prefixオプションを渡せます
bucket.objects(prefix: "uploads/tmp").each do |object|
  puts object.key
end

Aws::S3::Client#list_objects_v2 を使う場合

# https://docs.aws.amazon.com/sdk-for-ruby/v3/api/#Paging_Responses から引用
# yields one response object per API call made, this will enumerate
# EVERY object in the named bucket

# Aws::S3::Client#list_objectsやAws::S3::Client#list_objects_v2を使うケース
s3 = Aws::S3::Client.new

s3.list_objects(bucket:'aws-sdk').each do |response|  # eachで回せば取得できます!
  puts response.contents.map(&:key)
end

どちらの場合も、対象のオブジェクト(複数)そのものか、keyを配列に格納しておきます。

取得した複数のオブジェクトを一括で消したい

では、実際に削除となります。

基本: オブジェクトのキー(配列)を渡して削除していく

後述しますが、削除の場合も一括操作は1000件まで の制約がありますので注意してください。

# object_keys_list は、 こんな配列が入っている想定
# [ { "Key": "test1.txt" }, { "Key": "test2.txt" } .....  { "Key": "test2000.txt" } ]

# 1000個までが一括削除の上限なので、分割します
object_keys_list.each_slice(1000) do |object_keys|
  # Aws::S3::Bucketのインスタンスに対してdelete_objectsを呼び出す
  bucket.delete_objects(delete:{ objects: object_keys })
end

プレフィックス以下のオブジェクトを一括で消したい

上記は、キーをその分だけ特定しての削除でしたが、「ある一定のパス(ディレクトリ)以下は削除したい!」という場合はこちらも利用できます。
仮想的に、ディレクトリ(フォルダ)ごと削除するようなイメージです。

  • 仮想的なディレクトリの配下のオブジェクトを全部消したい場合
  • prefix指定でAws::S3::ObjectSummary::Collectionを取得し、batch_delete!を実行する
    • 1000件以上ありそうな場合は注意!

# ここではAws::S3::Resourceを使います
s3_resources = Aws::S3::Resource.new(
  access_key_id: アクセスキーID,
  secret_access_key: シークレットアクセスキー,
  region: リージョン
)

bucket_name = '作業したいバケット名'

# Aws::S3::Bucketのインスタンスを取得
# https://docs.aws.amazon.com/sdk-for-ruby/v3/api/Aws/S3/Resource.html#bucket-instance_method
bucket = s3_resources.bucket(bucket_name)

# 例: CarrierWave & Fogを使う場合、アップロードの際には uploads/tmp/ というディレクトリにもキャッシュが溜まる
prefix = "uploads/tmp/"

# "uploads/tmp/" を対象にする
# https://docs.aws.amazon.com/sdk-for-ruby/v3/api/Aws/S3/Bucket.html#objects-instance_method
# prefix指定をすると、その配下のオブジェクトを含んだコレクションを返す
target_s3_directory = bucket.objects({prefix: prefix})

# 取得したAws::S3::ObjectSummary::Collection に対して batc_delete! を呼ぶ
# https://docs.aws.amazon.com/sdk-for-ruby/v3/api/Aws/S3/ObjectSummary/Collection.html

target_s3_directory.batch_delete!

ここでの実際の処理は?

  • batch_delete! の処理では、プレフィックス配下のオブジェクトのkeyをループで取得し、「削除対象の実際のオブジェクトの配列」に格納します
# こんなパラメータを作成します
{ "Objects": [ { "Key": "test1.txt" }, { "Key": "test2.txt" } ],
  "Quiet": false
}

AWS CLIの操作の、こちらに該当します。

# Aws::S3::Client#delete_objectsを実行
resp = client.delete_objects({
  bucket: "examplebucket", 
  delete: {
    objects: [
      {
        key: "objectkey1", 
      }, 
      {
        key: "objectkey2", 
      }, 
    ], 
    quiet: false, 
  }, 
})

注意点

CLIでも、ruby sdkでも、一括削除は1000件が上限のため、1000件以上ある場合は分割が必要になります。

AWS CLI / ruby sdkの注意書き

The request contains a list of up to 1000 keys that you want to delete. In the XML, you provide the object key names, and optionally, version IDs if you want to delete a specific version of the object from a versioning-enabled bucket. For each key, Amazon S3 performs a delete operation and returns the result of that delete, success, or failure, in the response. Note that if the object specified in the request is not found, Amazon S3 returns the result as deleted.

REST APIの場合はJSONではなくXMLで削除対象を渡す形になります。

実際の作業にあたって

わたしは心配性なので、特に削除の場合は、Rubyのコード内だけでなく、AWS CLIも使って、想定のオブジェクトが正しく取得できるか、削除できているかをチェックしています。

また、削除前にCLIで検証用にオブジェクトを退避(バックアップ)させたりしています。
もちろんうまく行くと、とても嬉しいです!

まとめ

以上の通りですが、「1000件」の制約を面倒とみるかどうか。
個人的には、「間違ってちがうものを消しちゃった!!!!」という惨事を食い止めることができるので、良い意味でガードがかかっていると思いました。

SDKはソースが公開されていますし、対応するAPIもドキュメントがあるので、こんなわたしでも読みやすいなあと感じています!
(もちろん公式だけでなく、先に知見を公開してくださっている皆さんのおかげです)

また、誤字や認識間違いなどあれば、ご指摘いただけると幸いです。

6
7
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
6
7