☝️

【C#】ファイルをアップロードしてS3に保存するWebAPI

2023/06/19に公開

はじめに

.NETのWebAPIを開発する際、JSONでやり取りするものはよく作るんですが、ファイルをやり取りするものはどうやって作るんだっけ?となりました。
簡単に検証したので、フロントエンド・バックエンド両方の実装を掲載します。

概要

フロントエンドからバックエンドAPIへ画像をアップロードします。バックエンドではAWSのS3にファイルを保存します。

  • フロントエンド
    Next.jsで実装しています。axiosを使用してバックエンドAPIへ画像ファイルをPOSTします。
  • バックエンド
    .NET6で実装しています。フロントエンドから受け取った画像ファイルをS3に保存します。

フロントエンドの実装例

画像ファイルとしてjpegファイル(Blob)を準備しています。canvasに描画した内容をtoBlobを使用してjpegファイルにしています。
FormDataを作成し、BlobをセットしてPOSTします。

index.tsx
import axios from "axios";
import type { NextPage } from "next";
import { useEffect, useRef } from "react";

const Home: NextPage = () => {
  const canvasRef = useRef<HTMLCanvasElement>(null);

  // BlobファイルをバックエンドへPostする
  const postFile = async (blob: Blob | null) => {
    if (blob == null) return;

    const form = new FormData();
    form.append("upFile", blob);

    const res = await axios.post("http://localhost:5217/MyUpload", form);
    console.log(res);
  };

  const handleUpload = async () => {
    // canvas画像をjpegに変換 品質90%指定
    canvasRef.current?.toBlob((blob) => postFile(blob), "image/jpeg", 0.9);
  };

  useEffect(() => {
    // テスト用画像を準備
    const context = canvasRef.current?.getContext("2d");
    if (context != null) {
      context.fillStyle = "green";
      context.fillRect(0, 0, 320, 240);
      context.font = "50px Roboto medium";
      context.fillStyle = "black";
      context.fillText("画像のUpload", 0, 50);
    }
  }, []);

  return (
    <>
      <canvas ref={canvasRef} width="320" height="240" />
      <button onClick={handleUpload}>アップロード</button>
    </>
  );
};

export default Home;

バックエンドの実装例

Formデータを受信するため、入力モデルに[FromForm]を指定します。また、ファイルはIFormFile型を使用します。IFormFileを使用するとStreamを取得出来たり、ContentTypeを取得出来たり便利です。
S3へのアクセスはAWSSDKを使用しています。

MyUploadController.cs
using Amazon.S3;
using Amazon.S3.Model;
using Microsoft.AspNetCore.Cors;
using Microsoft.AspNetCore.Mvc;

namespace MyUploadApi.Controllers
{
    [ApiController]
    [Route("[controller]")]
    public class MyUploadController : ControllerBase
    {
        public MyUploadController(){}

        [EnableCors("CorsPolicy")]
        [HttpPost(Name = "MyUpload")]
        public async Task<UploadOutput> MyUpload([FromForm] UploadInput input)
        {
            // オブジェクトのキーを生成
            var objKey = Guid.NewGuid().ToString("N");

            // S3へ保存
            var client = new AmazonS3Client();
            var response = await client.PutObjectAsync(new PutObjectRequest
            {
                BucketName = "somebucketname",
                Key = objKey,
                InputStream = input.UpFile.OpenReadStream(),
                ContentType = input.UpFile.ContentType,
            });

            // S3に保存成功
            if (response.HttpStatusCode == System.Net.HttpStatusCode.OK)
            {
                // オブジェクトのキーを返却
                return new UploadOutput()
                {
                    Key = objKey,
                };
            }

            // S3に保存失敗
	    // TODO: エラー原因をログに出したり、フロント側にエラーを伝えること
            return new UploadOutput();
        }
    }

    public class UploadInput
    {
        public IFormFile UpFile { get; set; } = null!;
    }

    public class UploadOutput
    {
        public string Key { get; set; } = string.Empty;
    }
}

まとめ

ファイルのアップロードをするフロントエンド・バックエンドの実装でした。
どなたかの参考になれば幸いです。

Base64形式でアップロードする方法もあるよ

今回、toBlobを使用してBlob画像ファイルをPOSTしましたが、toDataURLを使用してBase64形式の画像をPOSTする方法もあります。
Base64形式に変換すると、画像データは文字列となるためJSONで扱うことができます。JSONとしてGETやPOSTができるため利便性が高いですが、Blobと比べデータサイズが大きくなってしまいます。
ネットワーク・ストレージを節約する観点ではBlob画像ファイルで転送する方がおすすめです。

画像ファイルをS3からダウンロードするWebAPI

ダウンロードするAPIの作り方は下記にまとめています。
https://zenn.dev/junnuj/articles/f37c40d3419fe3

Discussion