.NET CoreでアップロードされたファイルをS3に転送する

というわけで、表題通りのことをやっていくよ。
なんでもいいけど、最近はサーバー周りしかも.NET Coreのことしか書いてない気がする。気のせいかもしれないけど

実装環境

・.NET Core 2.2(3系でも動作可能な想定)
・Amazon S3

バケットを作成する

任意の名前でバケットは作成しておきます。この時、S3に転送したファイルをパブリックアクセス可能にしたい時(アップした画像のURLから画像を表示したい、など)は、 パブリックアクセスを許可しておきます。
自分の場合は、.NET Coreからアプロードするファイルだけ公開したかったので、公開権限を委任してアップロード時にパブリックアクセスを許可できるように設定しました。 具体的には、

・新しいアクセスコントロールリスト(ACL)を介して許可されたバケットとオブジェクトへのパブリックアクセスをブロックする
・任意のアクセスコントロールリスト(ACL)を介して許可されたバケットとオブジェクトへのパブリックアクセスをブロックする

だけオフに設定、です。

S3への書き込みができるIAMを作成

.NET CoreからS3へアップロードするには、putObject、putObjectACLの2つの権限を持ったIAMを作成し、そのIAMのAccesssKeyIDとSecretAccessKeyが必要になります。 IAMの作り方はググればたくさんヒットするので、ここでは割愛します。

AWSSDK.S3をインストール

NuGetパッケージから簡単にインストールができます。とりあえず最新版でOKでした。

C#実装例

実装例は、HTML上のフォームからファイルをアップロードするという想定で実装している。

public IActionResult UploadToS3(IFormFile file)
{
    if (file.Length <= 0) RedirectToAction("Index");

    string bucketName = "XXXXXXX";
    string keyName = DateTime.Now.ToString("yyyyMMdd-HHmmss") + Path.GetExtension(file.FileName);
    string accessKeyID = "your access key id";
    string secretAccessKey = "your secret access key";

    try
    {
        IAmazonS3 s3Client = new AmazonS3Client(accessKeyID, secretAccessKey, Amazon.RegionEndpoint.USWest2);
        var fileTransfer = new TransferUtility(s3Client);
        var fileTransferRequest = new TransferUtilityUploadRequest
        {
            BucketName = bucketName,
            InputStream = file.OpenReadStream(),
            Key = keyName,
            CannedACL = S3CannedACL.PublicRead
        };
        fileTransfer.UploadAsync(fileTransferRequest).GetAwaiter().GetResult();

    }
    catch (AmazonS3Exception s3e)
    {
        _logger.LogError("[UploadToS3] S3 error.");
        _logger.LogError("[UploadToS3] " + s3e.Message);
        RedirectToAction("Index");
    }
    catch (Exception e)
    {
        _logger.LogError("[UploadToS3] CSharp error");
        _logger.LogError("[UploadToS3] " + e.Message);
        RedirectToAction("Index");
    }

    ViewBag.upload_url = "https://" + bucketName + ".s3-us-west-2.amazonaws.com/" + keyName;
    return View();
}
syntax2html

S3CannedACL.PublicReadで公開可能に

バケット作成時に公開権限を委任するようにしたので、プログラム側からパブリックアクセス可能と指定ができます。
ViewBag.upload_urlに、アップロードしたファイルのURLを渡しているので、このURLをimgタグか何かのsrc属性に指定してやれば、 アップロードしたファイルを表示できるはず。(画像ファイルで動作確認済み)

413 Entity Too Largeで怒られた!

要は、アップロードするファイルサイズがnginxの上限を超えてしまったってこと。
nginx.confファイルにてclient_max_body_sizeの数値を調整すれば解決する