【C#/ASP.NET】ファイルアップロードを扱うAPIを作り、Swaggerで動作確認する

# 概要
* ASP.NET Web APIでmultipart/form-dataでファイルアップロードを扱う方法の実装を示す。
* Swaggerを用いてファイルアップロードAPIの動作確認を行う手順を示す。

# 実現したいこと
(1)Swaggerから画像データをmultipart/form-dataで送信する。
(2)サーバサイドでmultipart/form-dataを解析し、画像情報をローカルに格納する。

# 動作確認環境
Visual Studio 2019
ASP.NET Webアプリケーション(.NET Framework4.6.2)
Nugetパッケージ
・Swashbuckle V5.6.0(swagger)
Internet Explore ver21H1


# 目次

[ファイルアップロードのWeb Apiを実装する](#ファイルアップロードを扱うWeb Apiを実装する)
* [1.ソリューションを作成する](#1ソリューションを作成する)
* [2.Swaggerを設定する](#2swaggerを設定する) 
* [3.WebApiを実装する](#3WebApiを実装する)
* [4.Swaggerで動作確認する。](#4swaggerで動作確認する) 


# ファイルアップロードを扱うWeb Apiを実装する

## 1.ソリューションを作成する
(1)ASP.NET Webアプリケーションを選択する。
  ここでは[MultiPartServer]って名称で作っています。名前は任意です。
(2).NET Frameworkのバージョンを選択する。
　 今回は.NET Framework4.6.2を選択した。
(3)Web APIを選ぶ。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/156594/c79b962e-6bad-c4ac-0d87-df7233709797.png)

## 2.Swaggerを設定する
(1)Nugetパッケージをインストールする。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/156594/a55775ef-e0ee-ae7d-6fc0-0f557c358457.png)

(2)プロジェクトのビルド設定を変更して、XMLファイルを出力するように変更する。
[ビルド]→[XMLドキュメントファイル]にチェックを入れる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/156594/95c33cad-afaf-70bb-c3b7-544db347be5c.png)

(3)「ソリューションエクスプローラ」で「App_Start直下」の[SwaggerConfig.cs]を開く。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/156594/4608ba64-2f0d-8628-4d4f-25c9b6a8ed74.png)

(3)自動生成コードを下記のように修正する。

~~~C#:SwaggerConfig
using System.Web.Http;
using WebActivatorEx;
using MultiPartServer;
using Swashbuckle.Application;
using System.IO;

[assembly: PreApplicationStartMethod(typeof(SwaggerConfig), "Register")]
namespace MultiPartServer
{
    public class SwaggerConfig
    {
        public static void Register()
        {
            var thisAssembly = typeof(SwaggerConfig).Assembly;

            GlobalConfiguration.Configuration
                .EnableSwagger(c =>
                    {
                        c.SingleApiVersion("v1", "MultiPartServer");
                        //XMLファイルを読込む
                        c.IncludeXmlComments(GetXmlCommentsPath());
                    })
                .EnableSwaggerUi(c =>
                    {
                    });
        }
        private static string GetXmlCommentsPath()
        {
#warning パスはXMLドキュメントファイルの設定に合わせて修正すること
            return Path.Combine(System.AppDomain.CurrentDomain.BaseDirectory, "bin", "MultiPartServer.xml");
        }
    }
}
~~~

(4)Swagger UI画面用にフィルタ設定を適用する。
下記ソースを追加する。

~~~C#:UploadFileOperationFilter.cs
using System.Web.Http.Description;
using Swashbuckle.Swagger;

namespace MultiPartServer.Controllers
{
    /// <summary>
    /// Swagger　UI用ファイルアップロードフィルタ
    /// </summary>
    public class UploadFileOperationFilter : IOperationFilter
    {
        public void Apply( Operation operation, SchemaRegistry schemaRegistry, ApiDescription apiDescription )
        {
            operation.consumes.Add("multipart/form-data");
            operation.parameters = new[]
            {
                new Parameter()
                {
                    name = "File",
                    @in = "formData",
                    required = true,
                    //type=file設定は必須
                    type = "file",
                    description = "UploadFile"
                },
            };
        }
    }
}
~~~

## 3.WebApiを実装する
(1)「ソリューションエクスプローラ」で「Controllers」を選択する。
(2)「追加」→「Web API　コントローラクラス」を選択してファイルを追加する。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/156594/dd3bb9b5-df08-b6dd-9ad4-d954f8d3eac1.png)

(3)追加したファイルを下記のように修正する。

~~~C#:
using System;
using System.Net;
using System.Net.Http;
using System.Web.Http;
using System.IO;
using System.Threading.Tasks;
using System.Web;
using Swashbuckle.Swagger.Annotations;

namespace MultiPartServer.Controllers
{
    /// <summary>
    /// ファイルアップロードのイベントハンドラ
    /// </summary>
    public class UploadController : ApiController
    {
        /// <summary>
        /// Postメソッド
        /// </summary>
        /// <returns>Httpステータスコード</returns>
        [SwaggerOperationFilter(typeof(UploadFileOperationFilter))]
        public async Task<IHttpActionResult> PostFile()
        {
            // multipart/form-data以外、サポート外のメディア種類(Http Status 415)を返す
            if(!Request.Content.IsMimeMultipartContent())
            {
                return StatusCode(HttpStatusCode.UnsupportedMediaType);
            }
            // multipart/form-dataを保存する場所を指定する
            var root = HttpContext.Current.Server.MapPath("~/App_Data");
            var provider = new MultipartFormDataStreamProvider(root);

            try
            {
                //データを読み取る
                await Request.Content.ReadAsMultipartAsync(provider);
                foreach(var file in provider.FileData)
                {
                    //ファイルに格納する
                    var fileInfo = new FileInfo(file.LocalFileName);
                }
            }
            catch(Exception e)
            {
                return InternalServerError(e);
            }
            return Ok();
        }
    }
}
~~~

ポイントは[MultipartFormDataStreamProvider][7]クラス。
アップロードされたmultipart/form-dataをファイルストリームとして読み込むことができる。

## 4.Swaggerで動作確認する。

(1)デバック実行する。
(2)URL欄に[/swagger]を追記して、Enterを押す。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/156594/c874ef98-1d5f-8f49-34db-7706c25d5984.png)
※por番号はご自身の環境のものが表示されます。

(3)下記のようなSwagger UI画面が表示されることを確認する。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/156594/dcd4bdba-7fcf-a5f7-4b22-49a438f61411.png)

(4)UploadのPostメソッドのタブを開く。
(5)送信したいファイルを選択し、データを送信する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/156594/a7b16780-da5e-6d8c-810e-df9bc1494d55.png)
　(5-a)送信したいファイルを[Value]の項目で選択する。
　今回は下記のような単純なテキストデータを送った。

~~~C#:Test.txt
ABCD
~~~
　(5-b)[Try it out]を押下する。

(6)Http応答を確認する。
ステータコードが200なら、問題なし。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/156594/0a44fc03-c730-f209-f6ad-5ef1c58994c7.png)

(7)送信した画像情報が指定したフォルダに格納されていることを確認する。
 (7-a)Visual Studioの「ソリューションエクスプローラ」で「App_Data」を選択する。
右クリックして、[エクスプローラでファルダを開く]を選択する。
　(7-b)開かれたエクスプローラにファイルが格納されていること。
　下記のように送信情報が変な名前のファイルが作成されている。
　![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/156594/888c1fc8-264b-2360-89eb-511cb399c116.png)
 (7-c)テキストエディタで格納されたファイルを開く。
<b>送信内容と一致してれば、問題なし。</b>

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/156594/48d0941f-afcd-3803-abf6-87c6c375cbb0.png)

# サンプル実装のリンク
Gitに実装をアップしています。
[リンク](https://github.com/SY81517/Quiita/tree/master/StudyMultiPart/MultiPartServer)

# 参考

| No | リンク | 説明 |
|:-|:-|:-|
|1   |[ASP.NET Web Apiガイダンス][1]   |「ASP.NET Web API での HTML フォームデータの送信: ファイルのアップロードとマルチパート MIME」を参考にした。   |
|2   |[MSDNコードギャラリー][2]   |参考 No1のソースコードのダウンロードリンクが切れており、Githubからガイダンスのソースを探したもの。|
|3   |[しばやんさんの雑記][3]   |記事「ASP.NET Web API でアップロードされたファイルを扱う方法を知らなかったので調べた」を参考にした。   |
|4  |[@volpe28vさんの記事][4]   |Qiitaの記事「ASP .Net MVC 4 WebAPI のPOSTでマルチパートのファイルを送受信する」   |
|5   |[TonyTonyKunさんの記事][5] |「ファイルをアップロードする API の Swagger ドキュメントを書く」を参考にした。   |
|6   |[How to upload multipart/form-data in web api 2][6]   |英語でmultipart/form-dataでファイルアップロードを扱う方法が記載されている。   |
|7   |[MultipartFormDataStreamProviderクラス][7]   |MSDNのクラス仕様書|
|8   |[File Upload][8]   |Swagger file Upload|


[1]:https://docs.microsoft.com/ja-jp/aspnet/web-api/overview/advanced/sending-html-form-data-part-2
[2]:https://github.com/microsoftarchive/msdn-code-gallery-community-a-c/tree/master/ASP.NET%20Web%20API%20File%20Upload%20and%20Multipart%20MIME/%5BC%23%5D-ASP.NET%20Web%20API%20File%20Upload%20and%20Multipart%20MIME/C%23
[3]:https://blog.shibayan.jp/entry/20150319/1426750361
[4]:https://qiita.com/volpe28v/items/b86e0bc9db8e42688cc3
[5]:https://gooner.hateblo.jp/entry/2016/09/26/060154
[6]:https://bikeshsrivastava.blogspot.com/2016/09/part-36how-to-upload-multipartform-data.html
[7]:https://docs.microsoft.com/ja-jp/previous-versions/aspnet/hh835949(v=vs.118)
[8]:https://swagger.io/docs/specification/2-0/file-upload/