LoginSignup
19
4

More than 3 years have passed since last update.

AWS S3で1000件以上のオブジェクトを操作する方法

Last updated at Posted at 2020-12-02

こんにちは。フューチャー株式会社TIG所属2020年新卒の大西です。
フューチャー Advent Calendar 2020 3日目を担当します。

現在業務でAWS(特にS3やGlue Jobなど)を使うことが多いのため、私自身の業務で躓いた経験を元に 「AWS初心者が躓きやすいポイントとその解消法」 というテーマでブログを書いていきます。

まず、今日扱うテーマは 「Boto3を用いた1,000件以上のS3オブジェクトの操作」 です。

躓きポイントの概要

Boto3はPythonを介してAWSを操作するためのライブラリです。

Boto3を用いてAWSを操作する方は、 list_objects_v2objects.filter 等の関数を使って複数のオブジェクトを取得する機会があるのではないでしょうか。

list_object_v2 をもちいたオブジェクトの取得例

get_objects.py
import boto3
s3 = boto3.resource("s3")
bucket = s3.Bucket(bucket_name)
prefix = "S3バケットへのパス"
objects = bucket.meta.client.list_objects_v2(Bucket=bucket.name, Prefix=prefix)

しかし、上記の関数には 「一度のリクエストで取得できるオブジェクトの数は1,000件まで」 というルールがあり、1001件目以降のオブジェクトは取得することができません。

tech_blog_list_object_v2.png

tech_blog_filter.png

Boto3の公式ドキュメント内のS3に関する記述部分を読むと、S3では下記の関数に同様のルールが存在しているようです。

  • get_bucket_replication()
  • list_bucket_analytics_configurations()
  • list_bucket_inventory_configurations()
  • list_bucket_metrics_configurations()
  • list_multipart_uploads()
  • list_object_versions()
  • list_objects()
  • list_objects_v2()
  • list_parts()
  • put_bucket_analytics_configuration()
  • put_bucket_inventory_configuration()
  • put_bucket_lifecycle_configuration()
  • put_bucket_metrics_configuration()
  • put_bucket_replication()
  • object_versions.filter()
  • objects.filter()
  • BucketLifecycleConfiguration.put()

解決方法

今回は例として list_objects_v2 を用いる場合の解決方法を書きます。

list_objects_v2の返り値のdictには、結果が切り捨てられているかどうかを示す IsTruncated が含まれています。
そして、取得するオブジェクト数の上限を指定せずlist_objects_v2を実行し、取得したオブジェクトの数が1000件を超えていた場合は、objects["IsTruncated"]にTrueがセットされて返ってきます。
また、list_objects_v2は引数のStartAfterを設定することで、指定したS3ファイルの次のファイルからオブジェクトを取得してくれます。

よって、objects["IsTruncated"]==Trueだった場合は、1000件目のS3キーをStartAfterに設定して再度list_objects_v2を実行するという処理を["IsTruncated"]==Falseになるまで繰り返すことで、1,000件以上のオブジェクトでも取得することができます。

ソース

get_objects.py
s3 = boto3.resource("s3")
bucket = s3.Bucket(source_bucket)
prefix = "S3バケットへのパス"
keys = get_all_keys(bucket.name, prefix, [], "")


def get_all_keys(bucket_name: str, prefix: str, keys: List[str], marker: str) -> List[str]:
    """
    S3の指定したパスに存在するオブジェクトのキーを全て取得する

    Parameters
    ----------
    bucket_name: String
        対象のBucket
    prefix: String
        対象のディレクトリのパス
    keys: List[str]
    marker: String
        関数の中から呼び出す時のための引数。通常はkeys = [], marker = "" で呼び出す

    Returns
    -------
    List[str]
        取得したキーのリスト
    """
    s3 = boto3.resource("s3")
    bucket = s3.Bucket(bucket_name)
    objects = bucket.meta.client.list_objects_v2(Bucket=bucket.name, Prefix=prefix, StartAfter=marker)
    if "Contents" in objects:
        keys.extend([content["Key"] for content in objects["Contents"]])
        # 返り値のIsTruncatedがTrueかどうかを確認する
        if objects.get("isTruncated"):
            # marker引数に取得したkeysの末尾の値を設定して再度get_all_keysを実行する
            return get_all_keys(bucket_name=bucket_name, prefix=prefix, keys=keys, marker=keys[-1])
    return keys

最後に

今回は 「AWS初心者が躓きやすいポイントとその解消法」 の第一弾として IsTruncated を用いた1,000件上限の解決についてまとめました。

大量のオブジェクトを一度に取得したい方はぜひ参考にしてみてください。

19
4
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
19
4