LambdaでS3オブジェクトに独自メタデータの追加・更新を行う

# はじめに

LambdaでS3オブジェクトを制御したかったので、オブジェクトに独自のメタデータ(ユーザ定義メタデータ)を追加・更新して対処した。
本文では、ユーザ定義メタデータについて、実際のソースと実行結果について記す。

# ユーザ定義メタデータとは

Content-Lengthなどのオブジェクトの追加情報を`メタデータ`という。
メタデータには2種類あり、主にAWSシステム上で制御される`システムメタデータ`とユーザが独自で定義できる`ユーザ定義メタデータ`がある。
今回はファイルの上書き回数(メタデータ名を`x-amz-meta-count`とする)をユーザ定義メタデータとして定義する。

# Lambdaソース

下記LambdaをCloudWatchEventsなどをトリガーとして実行する。
指定したバケット、ディレクトリ配下のファイルを取得し、そのファイルのメタデータに`x-amz-meta-count`がなければ追加する。
あれば、メタデータの値を＋1する。
最後に追加更新した内容でファイルを上書き(copy_object())する。

```python

import os
import json
import boto3
import pprint
import urllib.parse

if os.getenv("AWS_SAM_LOCAL"):
    s3Client = boto3.client(
        's3', endpoint_url='http://host.docker.internal:4572/'
    )
    s3Resource = boto3.resource(
        's3', endpoint_url="http://host.docker.internal:4572/")
else:
    s3Client = boto3.client('s3')
    s3Resource = boto3.resource('s3')


def lambda_handler(event, context):

    BUCKET_NAME = "test-ksatou-20200125"
    PREFIX = "input/"

    try:
        # ディレクトリ下のオブジェクト一覧を取得
        objs = s3Resource.Bucket(BUCKET_NAME).objects.filter(Prefix=PREFIX)
        for obj in objs:
            print("Key: " + obj.key)

            # オブジェクトのメタデータを取得
            headObj = s3Client.head_object(Bucket=BUCKET_NAME, Key=obj.key)
            # 上記で取得したメタデータから、ユーザ定義メタデータ(x-amz-meta-count)を取得する
            # ユーザ定義メタデータのキーの接頭辞は「x-amz-meta-」で始まる
            httpHeaders = headObj["ResponseMetadata"]["HTTPHeaders"]
            count = httpHeaders.get("x-amz-meta-count")
            orgEtag = httpHeaders.get("etag")
            contentType = headObj["ContentType"]

            # ユーザ定義メタデータ(x-amz-meta-count)を追加・更新して、ファイルを上書く
            # メタデータの追加・更新の際は、接頭辞「x-amz-meta-」は記述する必要はない
            # valueはstring型でなければならない
            metadata = headObj["Metadata"]
            metadata["count"] = "0" if count == None else str(
                int(count) + 1)
            s3Client.copy_object(Key=obj.key, Bucket=BUCKET_NAME,
                                 CopySource={
                                     "Bucket": BUCKET_NAME, "Key": obj.key},
                                 Metadata=metadata,
                                 ContentType=contentType,
                                 MetadataDirective="REPLACE",
                                 CopySourceIfMatch=orgEtag)
    except Exception as e:
        print(e)
        raise e

```

## 実行結果

### 実施前

`x-amz-meta-count`は付与されていない。
![実施前.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545707/1946c01c-718d-46ff-41b0-251f03afe67c.png)

### 1回目

`x-amz-meta-count`が追加された。
![1回目実施.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545707/1648085f-5d4c-5d25-5db6-83d4889e3964.png)
![1回目結果.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545707/207818c2-b859-23d2-fd40-c294d2f1634d.png)



### 2回目

`x-amz-meta-count`の値が+1された。
![2回目実施.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545707/be9ad0b7-681a-a2ba-b4e3-b67ed85d920e.png)
![2回目結果.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545707/bc966d26-4fa8-8b8d-249d-9945cfd83b43.png)


# 参考

- [S3 オブジェクトにメタデータを追加する方法](https://docs.aws.amazon.com/ja_jp/AmazonS3/latest/user-guide/add-object-metadata.html)
- [AWS SAMでローカル環境でS3とDynamoDBを扱うLambdaを実行する](https://qiita.com/Yuki10/items/7a445a108b63b9298071#s3%E3%81%8B%E3%82%89%E3%81%AE%E8%AA%AD%E3%81%BF%E8%BE%BC%E3%81%BF%E5%87%A6%E7%90%86%E3%82%92%E5%AE%9F%E8%A3%85)
- [Boto3 S3既存のオブジェクトのメタデータを更新する](http://ja.voidcc.com/question/p-tlhhtbsn-hc.html)
- [How to update metadata of an existing object in AWS S3 using python boto3?](https://stackoverflow.com/questions/39596987/how-to-update-metadata-of-an-existing-object-in-aws-s3-using-python-boto3)