AWS Lambda Python S3でフォルダ以下のファイル一覧を取得する

いろいろハマったのでメモ

- ファイル情報を１回で取れない
- 一度に1000個まで
- Lambdaの15分制限を超えた
- S3にフォルダはない

## あるフォルダ以下のファイル一覧をCSVで出力

Lambdaのログでは行数に制限があるのでCSVで保存します。
拡張子 mp4 のみを検索します。
ファイル名、ファイルサイズ、更新日時も出力します。

```python:
S3Bucket    = 'bucket'
S3KeyPrefix = 'folder/'

s3 = boto3.resource('s3')
bucket = s3.Bucket(S3Bucket)
objs = bucket.meta.client.list_objects_v2(Bucket=bucket.name, Prefix=S3KeyPrefix)
           
csv = '{},{},{}\n'.format('file', 'size_mb', 'date')
for o in objs.get('Contents'):
    key = o.get('Key')
    size = o.get('Size')
    date = o.get('LastModified')
    if key[-4:]=='.mp4':
        csv += '{},{},{}\n'.format(key, int(size/1024/1024), date.strftime("%Y-%m-%d %H:%M:%S"))

s3_resource = boto3.resource('s3') 
s3_resource.Object(S3Bucket, 'file_list.csv').put(Body=csv)  
```

## S3のフォルダ一覧（ファイル数、サイズ、更新日時付き）

S3のフォルダ一覧を出力します。
サブフォルダは含みません。
フォルダ名、ファイル数、サイズ、一番新しい更新日時を出力します。

```python:
S3Bucket    = 'bucket'
S3KeyPrefix = 'folder/'

s3 = boto3.resource('s3')
bucket = s3.Bucket(S3Bucket)
objs = bucket.meta.client.list_objects(Bucket=bucket.name, Prefix=S3KeyPrefix, Delimiter='/')

csv = '{},{},{},{}\n'.format('folder', 'count', 'size_mb', 'date')
for o in objs.get('CommonPrefixes'):
    folder = o.get('Prefix')
    files = bucket.meta.client.list_objects_v2(Bucket=bucket.name, Prefix=folder)
    
    total_size = 0
    file_count = 0
    last_date = datetime(2000, 1, 1)
    for f in files.get('Contents'):
        if f.get('Key')[-1:]!="/":          
            total_size += f.get('Size')
            file_count += 1
            date = datetime.strptime(f.get('LastModified').strftime("%Y-%m-%d"), "%Y-%m-%d")
            if last_date<date:
    	        last_date=date
    
    csv += '{},{},{},{}\n'.format(folder, file_count, int(total_size/1024/1024), last_date.strftime("%Y-%m-%d"))

s3_resource = boto3.resource('s3') 
s3_resource.Object(S3Bucket, 'folder_list.csv').put(Body=csv) 
```

## S3のフォルダ一覧（1000個以上に対応）

list_objectsは上限1000個のため、objects.all()を使用します。
2階層（/2本）までを切り取りリストに追加します。

```python:lambda.py
from boto3 import Session

S3Bucket    = 'bucket'
S3KeyPrefix = 'folder/'

folder_list = []
sbucket = Session().resource('s3').Bucket(S3Bucket)
keys = [obj.key for obj in sbucket.objects.all()]    
for k in keys:
    ks = k.split('/')
    if len(ks)>=2:
        k2 = ks[0] +'/'+ ks[1]+'/'
        if (k2 in folder_list) == False:
            folder_list.append(k2)
```

## S3のフォルダ一覧（1000個以上、Lambda15分制限に対応）

ファイル数が多いと上記のobjects.all()ではlambdaの最大の15分を超えました。
以下のコードでは、folder/a...検索、folder/b...検索を繰り返します。
大文字Aは不可です。a以下が1000個の場合は不可です。

```python:
preList = ['0','1','2','3','4','5','6','7','8','9','0']
preList += ['a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z']

folder_list = []
for pre in preList:
    objs = bucket.meta.client.list_objects_v2(
        Bucket=S3Bucket,
        Prefix=S3KeyPrefix + pre,
        Delimiter='/'
    )
    if objs.get('CommonPrefixes') is None:
        continue
    for o in objs.get('CommonPrefixes'):
        folder_list.append(o.get('Prefix'))
```

## まとめ
3年くらい実運用で使っているS3で、不要データを削除するスクリプトを作ることになりました。削除といっても保険として、Trashフォルダに移動（コピー＆削除）をします。削除の前段で、ファイルやフォルダ、サイズ、日時をリスト化することから始めました。まさか一覧取得に15分もかかるとは思いませんでした。やはり本番環境は色々なことが起きます。