S3アップロードをトリガーに画像をLambdaでDropbox Businessにアップロードする

[LambdaでS3にある画像をDropbox Businessにアップロードする](https://qiita.com/nkmk1215/items/95e2d97e5bb89a8b904a)を改修し、
トリガーをS3アップロードに変更したときの個人メモ

[(参考)チュートリアル: Amazon S3 トリガーを使用して Lambda 関数を呼び出す](https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/with-s3-example.html)

---

# ソース修正
- 以下のようにソースを改修

```lambda_function.py
import os
import logging
import boto3
import urllib.parse
import base64
import requests
import json

# ログ設定
logger = logging.getLogger(__name__)
logger.setLevel(os.environ['LOG_LEVEL'])

# Dropbox設定
# ルートフォルダを指定
NAMESPACE_ID = os.environ['NAMESPACE_ID']
APP_KEY = os.environ['APP_KEY']
APP_SECRET = os.environ['APP_SECRET']
REFRESH_TOKEN = os.environ['REFRESH_TOKEN']



# S3から指定したバケット、フォルダ、ファイル名の画像を取得する
# 取得できる画像サイズはLambdaのメモリーに依存
# return:画像のバイナリデータ
def get_img_from_s3(bucket_name, file_path):
    logger.debug('start get_img_from_s3()')

    s3 = boto3.client('s3')
    response = s3.get_object(Bucket=bucket_name, Key=file_path)
    body = response['Body'].read()

    logger.debug('end get_img_from_s3()')

    return body


# リフレッシュトークンからアクセストークンを取得する
# return:アクセストークン
def get_token():
    logger.debug('start get_token()')

    data = {
      'grant_type': 'refresh_token',
      'refresh_token': REFRESH_TOKEN
    }
    
    response = requests.post('https://api.dropbox.com/oauth2/token', data=data, auth=(APP_KEY, APP_SECRET))
    logger.info('response:' + json.dumps(response.json(),default=str))

    access_token = response.json()['access_token']
    logger.debug('access_token:' + access_token)

    logger.debug('end get_token()')

    return access_token


# 画像をDropboxにアップロードする
# img:画像のバイナリデータ
# return:アップロード結果
def upload_to_dropbox(access_token, img, file_path):
    logger.debug('start upload_to_dropbox()')

    arg = '{"path": "' + '/' + file_path + '","mode": "add","autorename": true,"mute": false,"strict_conflict": false}'

    headers = {
        'Authorization': 'Bearer ' + access_token,
        'Dropbox-API-Arg': arg.encode("utf_8"),
        'Content-Type': 'application/octet-stream',
        'Dropbox-API-Path-Root': '{".tag": "namespace_id", "namespace_id": "' + NAMESPACE_ID + '"}',
    }

    response = requests.post('https://content.dropboxapi.com/2/files/upload', headers=headers, data=img)
    logger.info('response:' + json.dumps(response.json(),default=str))

    logger.debug('end upload_to_dropbox()')

    return json.dumps(response,default=str)


def lambda_handler(event, context):
    logger.debug('start lambda_handler()')

    # S3設定
    bucket_name = event['Records'][0]['s3']['bucket']['name']
    logger.info('bucket_name:' + bucket_name)

    # 画像ファイル情報
    file_path = urllib.parse.unquote_plus(event['Records'][0]['s3']['object']['key'], encoding='utf-8')
    logger.info('file_path:' + file_path)

    img = get_img_from_s3(bucket_name, file_path)
    access_token = get_token()
    result = upload_to_dropbox(access_token, img, file_path)

    logger.debug('end lambda_handler()')

    return result
```

# テストイベントJSON設定
- 以下のようにテストイベントJSONを設定
    - バケット名とオブジェクトキー以外はテンプレートのまま利用

```JSON
{
  "Records": [
    {
      "eventVersion": "2.0",
      "eventSource": "aws:s3",
      "awsRegion": "us-west-2",
      "eventTime": "1970-01-01T00:00:00.000Z",
      "eventName": "ObjectCreated:Put",
      "userIdentity": {
        "principalId": "EXAMPLE"
      },
      "requestParameters": {
        "sourceIPAddress": "127.0.0.1"
      },
      "responseElements": {
        "x-amz-request-id": "EXAMPLE123456789",
        "x-amz-id-2": "EXAMPLE123/5678abcdefghijklambdaisawesome/mnopqrstuvwxyzABCDEFGH"
      },
      "s3": {
        "s3SchemaVersion": "1.0",
        "configurationId": "testConfigRule",
        "bucket": {
          "name": "（バケット名）",
          "ownerIdentity": {
            "principalId": "EXAMPLE"
          },
          "arn": "arn:aws:s3:::example-bucket"
        },
        "object": {
          "key": "image/escle.png",
          "size": 1024,
          "eTag": "0123456789abcdef0123456789abcdef",
          "sequencer": "0A1B2C3D4E5F678901"
        }
      }
    }
  ]
}
```

- Testを実行し、動作が改修前後で差異がないことを確認

# トリガー設定
- 「トリガーを追加」を押下

![000.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242897/699dc59b-0f99-b1f0-2259-7839a62f22b4.jpeg)
 
- S3バケットを指定し、「追加」を押下

![001.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242897/b2458a29-5bef-e965-3138-326742345899.jpeg)

- 追加されたことを確認

![002.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242897/3b6588c6-fc30-e30b-aef2-df3909ae4b85.jpeg)


# 動作確認
- 任意のフォルダにアップロード

![003.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242897/fa6c42a0-4c10-7985-dcfd-5a4931a479ca.jpeg)
![004.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242897/dc33bfe0-c105-fa98-4221-59c9ab74533d.jpeg)

- アップロードされたことを確認

![005.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242897/169abebe-d10d-760e-bb1c-50957656c1b5.jpeg)

- Lambdaのログを確認

![006.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242897/021f6c58-e6e0-bade-cb4d-8eb99a01a56d.jpeg)

- Lambdaが実行されたことを確認

![007.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242897/ff8b29dd-60d6-8027-3c50-331d28eaefe7.jpeg)

- Dropboxにアップロードされたことを確認

![008.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242897/b6ab7f76-d5aa-388a-5f58-3941b438a310.jpeg)

# 感想
- テストイベントJSONを利用することで問題を切り分けながら改修できた
- 大量の画像がアップロードされたときの性能まわりは気になる
- エラー処理等も運用含めて考えていきたい