C#でS3をトリガーにファイルを移動するLambdaを書いてみた

AWS

はじめに

おつかされまです。tyamonです。

今回はC#でAWS Lambdaを書き、S3にファイルがアップロードされたら別のバケットに移動する処理を実装しました。PythonでLambdaを書いてきた方が、C#でどう変わるかも含めてまとめます。

結論:C#でもS3トリガーのLambdaは問題なく作れます。Pythonと違い .csproj へのパッケージ追加が必要な点だけ押さえれば詰まりません。


C#でLambdaを書くときにPythonと違う点

Pythonでは pip install boto3 するだけで使えますが、C#では2つのファイルを両方編集する必要があります。

  • Function.cs — 実際のコード(using Amazon.S3; で名前空間を参照)
  • *.csproj — プロジェクト設定ファイル。使うNuGetパッケージをここで宣言する

using をコードに書いても、.csproj にパッケージが登録されていないとコンパイラはライブラリを見つけられません。この2つがセットで初めて動きます。

また、S3操作は async/await を使う設計になっているため、ハンドラーも async Task で定義します。


C# Lambda S3 ファイル移動の実装手順

1. NuGetパッケージを追加する

*.csproj に以下を追加して dotnet restore を実行します。

<PackageReference Include="AWSSDK.S3" Version="3.7.414.2" />
<PackageReference Include="Amazon.Lambda.S3Events" Version="3.1.0" />

2. Function.cs を実装する

S3にはMoveがないため、コピー → 削除の2ステップで移動を実現します。

using Amazon.Lambda.Core;
using Amazon.Lambda.S3Events;  // S3Event型を使うために必要
using Amazon.S3;               // AmazonS3Client, IAmazonS3
using Amazon.S3.Model;

using Amazon.Lambda.Core;
using Amazon.Lambda.S3Events;  // S3Event型を使うために必要
using Amazon.S3;               // AmazonS3Client, IAmazonS3
using Amazon.S3.Model;

[assembly: LambdaSerializer(typeof(Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer))]

namespace MySecondLambda;

public class Function
{
    // S3クライアントをフィールドで保持することで、Lambdaの再利用時に使い回せる
    private readonly IAmazonS3 _s3Client = new AmazonS3Client();

    public async Task FunctionHandler(S3Event evnt, ILambdaContext context)
    {
        // 移動先バケット名を環境変数から取得(AWSコンソールのLambda設定で指定する)
        string destBucket = Environment.GetEnvironmentVariable("DEST_BUCKET")
            ?? throw new Exception("DEST_BUCKETが設定されていません");

        // 1つのS3イベントに複数ファイルが含まれる場合があるのでループ処理
        foreach (var record in evnt.Records)
        {
            string srcBucket = record.S3.Bucket.Name;
            // S3のキーはURLエンコードされているのでデコードが必要
            string key = Uri.UnescapeDataString(record.S3.Object.Key.Replace("+", " "));

            context.Logger.LogInformation($"移動: S3://{srcBucket}/{key} → S3://{destBucket}/{key}");

            // S3にはMoveがないため、コピー → 削除の2ステップで移動を実現する
            await _s3Client.CopyObjectAsync(srcBucket, key, destBucket, key);
            await _s3Client.DeleteObjectAsync(srcBucket, key);
        }
    }
}

3. aws-lambda-tools-defaults.json を設定する

デプロイ時に毎回聞かれないよう、以下を設定しておきます。

{
  "profile": "default",
  "region": "ap-northeast-1",
  "configuration": "Release",
  "function-runtime": "dotnet10",
  "function-memory-size": 512,
  "function-timeout": 30,
  "function-handler": "MySecondLambda::MySecondLambda.Function::FunctionHandler",
  "function-name": "MySecondLambda",
  "function-description": "S3イベントでトリガーされたファイルを別のS3バケットに移動する",
  "function-role": "arn:aws:iam::xxxxxxxxxxxx:role/ロール名"
}

4. デプロイする

dotnet lambda deploy-function

ビルド・ZIP化・アップロードまで自動で行われます。

5. AWSコンソールで設定する

デプロイ後、以下をコンソールで設定します。

  1. Lambda → 環境変数 → DEST_BUCKET に移動先バケット名を設定
  2. トリガーにS3(元バケット)を追加

ハマりポイント

using を書いただけでは動かない

using Amazon.S3; をコードに書いても、.csprojAWSSDK.S3 が入っていないと「型または名前空間の名前が見つかりません」エラーになります。新しいライブラリを使うときは .csproj への追加(または dotnet add package パッケージ名)とセットで覚えておくと詰まりません。

S3のキーはURLデコードが必要

日本語ファイル名や空白を含むキーはURLエンコードされた状態でイベントに入ってきます。Uri.UnescapeDataString でデコードしないとコピー先のキーがおかしくなります。


まとめ

C#でS3トリガーのLambdaを実装し、ファイルを別バケットに移動する処理を動かすことができました。Pythonと比べると .csproj の管理が増えますが、型があるおかげでIntelliSenseが効き、ミスに気づきやすいのはメリットです。


参考資料

コメント

タイトルとURLをコピーしました