Lambda PythonでS3のオブジェクト一覧を取得

Lambdaのコードを書く際は何かとS3のファイル一覧を取得するような処理が必要になることがあります。

boto3を利用してS3のファイル一覧の取得をしてみます。

boto3インストール

AWSサービスの操作を行うための「boto3」をインストールします。
※私の環境ではPythonはrootユーザーでインストールしています

# pip install boto3

AWSのロール作成

Lambdaを実行するためのロールを作成します。
ここではS3の操作のみ行うのでS3フルアクセスのポリシーを付与します。

  • ロール名: Lambda-Roll
  • ポリシー: AmazonS3FullAccess

サービス作成

まずはServerless Frameworkのサービスを作成します。

$ sls create --template aws-python3 --path s3-list

serverless-python-requirementsインストール

Pythonのモジュールを簡単に扱うことのできる「serverless-python-requirements」をインストールします。

$ sls plugin install -n serverless-python-requirements

serverless.yml編集

service: s3-list

provider:
  name: aws
  runtime: python3.7
  region: ap-northeast-1
  role: arn:aws:iam::************:role/Lambda-Roll
  deploymentBucket:
    name: 'sls-src'
    serverSideEncryption: AES256

plugins:
  - serverless-python-requirements

functions:
  S3List:
    handler: handler.execute
    description: "S3オブジェクト一覧取得"
  • serverless-python-requirementsのプラグインの設定を追加
  • リージョンは東京リージョンを指定
  • 事前に作成したロールのARNを設定
  • デプロイ時にS3に作成されるソースのバケットを指定
  • ファンクション名は「S3List」へ変更
  • ハンドラは「handler.execute」へ 変更
  • 説明も追加

handler.py編集

import boto3

def execute(event, context):

    bucket = event['bucket']
    prefix = event['prefix']

    s3 = boto3.client('s3')

    object_list = []
    next_token = ''

    while True:
        if next_token == '':
            response = s3.list_objects_v2(Bucket=bucket, Prefix=prefix)
        else:
            response = s3.list_objects_v2(Bucket=bucket, Prefix=prefix, ContinuationToken=next_token)

        if response.get('Contents') is None: return []
        for o in response.get('Contents'):
            object_list.append(o.get('Key'))

        if 'NextContinuationToken' in response:
            next_token = response['NextContinuationToken']
        else:
            break

    return object_list
  • boto3をインポート
  • S3のオブジェクト取得するためのバケットとプレフィックスはパラメータから取得
  • S3のオブジェクトは「list_objects_v2」メソッドを利用
  • 1000個以上のオブジェクトが存在した場合を考慮してNextContinuationToken があれば続きから再取得

実行

$ sls invoke local -f S3List -d '{"bucket":"my-bucket-******","prefix":"test"}'
[
    "test.txt",
    "test/",
    "test/test.txt",
    "test/test2.txt",
    "test/test3.txt",
    "test2/",
    "test2/test.txt",
    "test2/test/",
    "test2/test/test.txt",
    "test2/test/test2.txt"
]

ファイルだけでなくてフォルダ、その下の階層のファイルまで一気に取得しました。
フォルダや下の階層まで取得する必要がなければ「Delimiter=’/’」を指定すればオブジェクトのみが取得できます。

最後に

S3のオブジェクトを取得するだけでも癖がありかなりめんどくさいです。
S3の操作はよく使うので共通化したものがあるといいかもしれません。

参考