LoginSignup
5
5

More than 3 years have passed since last update.

S3ファイルをまとめてダウンロードできる環境を作る

Last updated at Posted at 2017-12-05

やりたいこと

  • S3ファイルをまとめてダウンロードしたい
    • AWS CLIを使えば解決
      • その環境がない人用の環境構築

仕様

  • AWS Lambda: Python 2.7
  • Lambdaを手動で実行したらS3に圧縮して保存
    • 対象の絞り込み(bucket、key、拡張子)と出力先はLambda内に記述
    • 想定外の大量ファイルを指定できないよう最大1000件まで(そもそも処理に使うlist_objectsの最大が1000件)
      • 1000件から先はlist_objectsのMarkerを設定して続きから処理する(名前の昇順で取得される)
  • 手動が嫌な場合は下記のような方法もあります
    • S3 notifications
      • 指定したS3の場所にファイルが追加、削除などされたら実行
    • CloudWatch Events
      • cronの時間指定で実行

Lambdaの処理時間

ファイル数 時間(ms)
10 2600
100 17100
1000 177500

※平均25KBの画像ファイル
上記の結果からLambdaのタイムアウトをを3秒→3分に設定しました

AWS料金

  • 月数十回なのでLambdaは無料範囲内
  • S3のダウンロード料金で数十円程度

Lambda

関数の作成

  • 名前: s3downloads
  • ランタイム: Python 2.7
  • ロール: テンプレートから新しいロールを作成
  • ロール名: Lambda_s3downloads
  • ポリシーテンプレート: 空欄(下記のIAMロールで追加)

IAM ロール

  • Lambda_s3downloadsロールを選択
  • ポリシーのアタッチから「AmazonS3FullAccess」を追加する
    • ※対象のS3を限定的にする場合はカスタムしてください

Lambdaのその他の設定

  • タイムアウト3秒から3分に変更
  • index.jsをlambda_function.pyにRename
  • コードの内容は下記のPythonコードを貼り付け
  • テストイベントは今回は使用しないのでデフォルトのまま保存して実行します

Pythonのコード

lambda_function.py
import boto3
import tarfile
import os

s3_client = boto3.client('s3')

in_bucket = 's3inbucket'
in_prefix = 'hoge/neko/'
in_extension = '.jpg'
in_max = 1000
in_marker = ''

out_bucket = 's3outbucket'
out_dir = 'out/'
out_filenmae = 'archive'


def lambda_handler(event, context):
    response = s3_client.list_objects(Bucket=in_bucket, Prefix=in_prefix, MaxKeys=in_max, Marker=in_marker)
    if 'Contents' not in response:
        return

    s3_files = [c['Key'] for c in response['Contents'] if c['Key'].endswith(in_extension)]

    if s3_files:
        get_s3_files(s3_files)
        create_archive()
        return s3_files


def get_s3_files(s3_files):
    for s3_file in s3_files:
        basename = os.path.basename(s3_file)
        tmp_file = os.path.join('/tmp/', basename)
        s3_client.download_file(in_bucket, s3_file, tmp_file)


def create_archive():
    out_file = os.path.join(out_dir, out_filenmae + '.tar.gz')
    archive_file = os.path.join('/tmp/', out_filenmae + '.tar.gz')

    archive = tarfile.open(archive_file, mode='w:gz')
    archive.add('/tmp/', arcname=out_filenmae)
    archive.close()

    s3_client.upload_file(archive_file, out_bucket, out_file)
    print('output ' + out_bucket + ':' + out_file)

変数の設定

変数 説明
in_bucket 対象のバケット
in_prefix 対象のバケットのキー
in_extension 対象の拡張子(空の場合は全て)
in_max 最大取得数(0〜1000)
in_marker 取得数を超えた場合、最後のキー(フルパス)をここに記述することで続きからの処理になる。※対象のフルパス一覧はテスト結果の詳細に出力されます
out_bucket 出力先のバケット
out_dir 出力先のディレクトリ
out_filenmae 圧縮ファイル名

Prefixで指定した階層だけを対象にしたい場合

下記のように「Delimiter='/'」を追加する

response = s3_client.list_objects(Bucket=in_bucket, Prefix=in_prefix, Delimiter='/', MaxKeys=in_max, Marker=in_marker)

毎回はまるところ

コメントに日本語が含まれているとエラー・・・

2019/08/13 追記

先頭に下記を記述することで日本語コメントをつけることができるようになる(emoseiさん教えていただきありがとうございます)

## coding: UTF-8

応用

S3ファイルをまとめてダウンロードできる環境をテストイベントの設定を利用して作る

参考

Boto 3 で Amazon S3 上の key を取得する方法、実装例、注意点

5
5
2

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