S3 から Google Drive へ Lambda を使ってファイルアップロード

# はじめに

この記事では、AWSのLambdaを使用してAmazon S3からGoogle Driveにファイルをアップロードする方法を紹介します。
Google Drive APIを活用することで、プログラム的にドライブ内のファイル操作が可能になります。

# S3 から Google Drive へ Lambda を使ってファイルアップロード

## Google プロジェクトの作成

画面を見ながら操作したい方は、[こちら](https://qiita.com/w2or3w/items/b66a4a8e45e6001a9c46)を参考して作業を進めて下さい。

1. **Google Drive API を有効にする**
    - Google Cloud Platformで新しいプロジェクトを作成し、Google Drive APIを有効にします。

2. **Google Cloud でサービスアカウントを作成する**
    - Google Cloud Platformでサービスアカウントを作成し、必要な権限を付与します。

3. **サービスアカウントの秘密鍵を作成（jsonダウンロード）**
    - 作成したサービスアカウントで秘密鍵（JSON形式）を生成し、ダウンロードします。

4. **Google Drive の共有するフォルダにサービスアカウントを紐付ける**
    - Google Driveで共有するフォルダを作成し、サービスアカウントをフォルダに紐付けます。URLの一番最後のキーも取得します。

## S3

1. **バケットを作成する**
    - AWS S3で新しいバケットを作成します。

2. **ファイルを配置する**
    - 作成したバケットにアップロードするファイルを配置します。

## Lambda

1. **Lambda Layer を作成する**
    - Lambda で`google-api`のライブラリを使用するために、ローカル環境でセットアップします。
    ```sh
    $ mkdir python && cd python
    $ pip install google-api-python-client -t .
    $ pip install oauth2client -t .
    $ zip -r python.zip python/
    ```
    - 作成した`python.zip`をLambda Layerにアップロードします。

2. **Lambda 関数を作成する**
    - 新しいLambda関数を作成し、作成したLambda Layerをアタッチします。
    - IAMロールにS3のアクセス権を設定します。（必要な権限のみ）
    - `service-account-key.json`をアップロードして、`lambda_function.py`と同じ階層に配置します。
    - `service-account-key.json`に**サービスアカウントの秘密鍵を作成（jsonダウンロード）**で取得したjsonの内容をコピペして下さい。
    - 以下のソースコードを`lambda_function.py`に貼り付けます。
    ```python
    import os
    import boto3
    from googleapiclient.discovery import build 
    from googleapiclient.http import MediaFileUpload 
    from oauth2client.service_account import ServiceAccountCredentials 

    def create_s3_client():
        """S3クライアントを作成する"""
        return boto3.client('s3')

    def download_file_from_s3(s3, bucket, key, local_path):
        """S3からファイルをダウンロードする"""
        s3.download_file(Bucket=bucket, Key=key, Filename=local_path)

    def create_google_drive_service():
        """Google Driveサービスを作成する"""
        scope = ['https://www.googleapis.com/auth/drive.file'] 
        keyFile = 'service-account-key.json'
        credentials = ServiceAccountCredentials.from_json_keyfile_name(keyFile, scopes=scope)
        return build("drive", "v3", credentials=credentials, cache_discovery=False)

    def upload_file_to_google_drive(service, file_name, local_file_path, folder_id):
        """Google Driveにファイルをアップロードする"""
        mime_type = get_mime_type(local_file_path)
        file_metadata = {"name": file_name, "mimeType": mime_type, "parents": [folder_id]}
        media = MediaFileUpload(local_file_path, mimetype=mime_type, resumable=True)
        service.files().create(body=file_metadata, media_body=media, fields='id').execute()

    def get_mime_type(file_path):
        """ファイルのMIMEタイプを取得する"""
        ext = os.path.splitext(file_path.lower())[1][1:]
        if ext == "jpg":
            ext = "jpeg"
        return "image/" + ext

    def handle_file_processing(bucket, key, folder_id):
        """ファイルの処理を行うメインの関数"""
        local_path = "/tmp/" + os.path.basename(key)
        s3_client = create_s3_client()
        google_drive_service = create_google_drive_service()

        try:
            download_file_from_s3(s3_client, bucket, key, local_path)
            upload_file_to_google_drive(google_drive_service, os.path.basename(key), local_path, folder_id)
        finally:
            clean_up_local_file(local_path)

    def clean_up_local_file(file_path):
        """ローカルファイルを削除する"""
        if os.path.exists(file_path):
            os.remove(file_path)

    def lambda_handler(event, context):
        try:
            bucket = os.environ.get('S3_BUCKET_NAME', 'default-bucket-name')
            key = event.get('key')
            folder_id = event.get('folder_id')

            print(f"Received file from S3: Bucket={bucket}, Key={key}, FolderID={folder_id}")
            handle_file_processing(bucket, key, folder_id)
        except Exception as e:
            print(f"Error in lambda handler: {e}")
            raise e
    ```

3. **テスト実施**

Lambda 関数の作成後、適切に機能するかを確認するためにテストを行います。
以下の手順に従ってテストを実施してください。

- **テストイベントを作成する**
    - Lambda関数のページで「テストイベント」を選択し、「新しいテストイベントを作成」をクリックします。
    - テストイベントに名前を付け、以下のJSON構造をボディにコピーします。

        ```json
        {
            "folder_id": "ここに取得したfolder_idを入力",
            "key": "sample.png"
        }
        ```
        - 「folder_id」の値は、「Google Drive の共有するフォルダにサービスアカウントを紐付ける」セクションで取得したfolder_id（フォルダのURLの一番最後のキー）を使用します。
        - 「key」の値は、S3バケット内のテストしたいファイル名を指定します。

- **テストを実行する**
    - テストイベントを保存した後、Lambda関数ページの「テスト」ボタンをクリックしてテストを実行します。

- **結果を確認する**
    - テスト実行後、結果がLambda関数ページの下部に表示されます。成功した場合、指定したファイルがGoogle Driveの指定したフォルダにアップロードされていることを確認してください。
    - エラーが発生した場合は、ログ出力を確認し、問題の原因を特定して修正します。

テストを通じて、Lambda関数が正しく機能していることを確認できます。
特に、Google Drive APIとの連携やS3からのファイルの取得など、各ステップが正しく動作しているかをチェックしてください。

# まとめ

この記事では、Google Drive APIを利用して、AWS LambdaとS3を組み合わせたファイルアップロードのプロセスを紹介しました。
サービスアカウントの設定からLambda関数の作成まで、具体的な手順を説明しました。
これにより、Google Driveへの自動化されたファイルアップロードが可能となり、より効率的なファイル管理が実現できます。

## 参考

- Lambda(Python)でGoogle Driveへファイルアップロード: [Qiita](https://qiita.com/w2or3w/items/b66a4a8e45e6001a9c46)
- Google Drive API の概要: [Google Developers](https://developers.google.com/drive/api/guides/about-sdk?hl=ja)
- Google Workspace API の有効化: [Google Developers](https://developers.google.com/workspace/guides/enable-apis?hl=ja)
- AWS Lambda Layerの作成方法をわかりやすく解説【Python3.9】: [Qiita](https://qiita.com/__DASHi__/items/268062f0dba0e93170f2)

---
### 会社紹介

**株式会社 Mosaica**  
最先端テクノロジーで社会課題を解決し、持続可能な未来を創造する IT カンパニー。
AI ソリューション、クラウド統合、DX 推進、経営コンサルティングなど包括的なサービスでビジネス変革を支援しています。  

詳しくは [公式サイト](https://mosaica.co.jp/) までお気軽にご相談ください。
公式サイト： https://mosaica.co.jp/