asp.net coreでサービス作ったから宣伝兼ねて技術公開する -2_5.ファイルアップロード画面→Amazon S3 アップロード-

##ファイルアップロードのサンプルコード

やっとここまで来ました。。。これで
さて、今回のサービスですが、写真を撮って、サーバへアップロードする必要がございます。

ファイルアップロードに関しては、Microsoft Docsにサンプルがございましたので、そのまま利用可能でした。

FileUploadSample：
~~https://github.com/aspnet/AspNetCore.Docs/tree/master/aspnetcore/mvc/models/file-uploads/sample/FileUploadSample~~

しかーし、今見てみたらなんとリンク切れ。

どうやら core 3系のサンプルと統合されたようで、こちらになります。
https://github.com/aspnet/AspNetCore.Docs/tree/master/aspnetcore/mvc/models/file-uploads/samples/2.x/SampleApp

またリンク先が変わるかもしれませんので、その際はAspNetCore.DocsをGitで検索ください。


##ファイルアップロードの実装
サンプルから一部抜粋し、Workへ実装しましょう！


```Views\Work\Index.cshtml
@{
    ViewData["Title"] = "Work Page";
}

<div class="text-center">
    <h1 class="display-4">Working Page</h1>
</div>

<hr>

<form method="post" enctype="multipart/form-data" asp-controller="Work" asp-action="FileUpload">
    <div class="form-group">
        <div class="col-md-4">
            <p>ファイルアップロード</p>
            <input type="file" name="files" multiple />
        </div>
        <div class="col-md-2">
            <input type="submit" value="Upload" />
        </div>
    </div>
</form>

```

次に、これを受け付けるコントローラ実装！


```Controllers\WorkController.cs
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using sample2_1.Models;

namespace sample2_1.Controllers
{
    public class WorkController : Controller
    {
        public IActionResult Index()
        {
            /* ログインされている場合、共通受け渡しデータにユーザ名を入れておく */
            if (User.Identity.IsAuthenticated)
            {
                var user_id = "";
                var user_nm = "";
                // ユーザIDの特定
                foreach (var i in this.User.Identities)
                {
                    foreach (var n in i.Claims)
                    {
                        if (n.Type == "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier")
                        {
                            user_id = n.Value;
                        }
                        if (user_id != "") break;
                    }
                    if (user_id != "") break;
                }

                // ユーザマスタから情報取得
                using (var db = new AppDbContext())
                {
                    // [出力]
                    foreach (var mUser in db.MUsers.Where(m => m.UserId == user_id))
                    {
                        user_nm = mUser.UserNm;
                        break;
                    }
                }

                ViewData["user_nm"] = user_nm;
            }



            return View();
        }

        [HttpPost("UploadFiles")]
        public async Task<IActionResult> FileUpload(List<IFormFile> files)
        {
            // 保存先を取得
            string filePath = @"C:\work\";

            // ファイル保存
            long size = files.Sum(f => f.Length);
            foreach (var formFile in files)
            {
                if (formFile.Length <= 0) continue;
                // ローカルに保存
                using (var stream = new FileStream(filePath + formFile.FileName, FileMode.Create))
                {
                    await formFile.CopyToAsync(stream);
                }
            }

            // 画面に返す返却値
            ViewData["uploadResult"] = Ok(new { count = files.Count, size, filePath }).Value.ToString();

            // ViewをIndex画面で返却
            return View("Index");
        }
    }
}
```

サンプルそのままなのですが、結果を画面に返したいので、ViewData["uploadResult"] へアップロード情報を返します。
ViewData["uploadResult"] に値が入っていた場合に、画面に表示するようにします。

```Views\Work\Index.cshtml
@{
    ViewData["Title"] = "Work Page";
}

<div class="text-center">
    <h1 class="display-4">Working Page</h1>
</div>

<hr>

<form method="post" enctype="multipart/form-data" asp-controller="Work" asp-action="FileUpload">
    <div class="form-group">
        <div class="col-md-4">
            <p>ファイルアップロード</p>
            <input type="file" name="files" multiple />
        </div>
        <div class="col-md-2">
            <input type="submit" value="Upload" />
        </div>
    </div>
</form>

@if (ViewData["uploadResult"] != null)
{
    <hr>
    <h5>ファイルアップロード結果　：　@ViewData["uploadResult"]</h5>
}
<hr>

```

##デバッグ実行

ログイン後、Work ページへ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/249830/7d23fdbe-05dc-2dc3-044a-dde5f4848bec.png)

写真を選択
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/249830/9941a9ab-070f-5481-2916-4efdc7bfe7ba.png)

画面にファイル名が中途半端に表示されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/249830/661b4844-1510-8191-f127-f0c888539813.png)

Uploadをクリック
正しくアップロード情報が画面に表示されたことを確認
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/249830/697a319a-7ed2-ea43-611f-abba8392c47d.png)

ローカスパス：C:\Work\ にファイルが存在しているかを確認
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/249830/0429a452-11ce-1795-3bf1-3989ce00c227.png)

OK！


一応できるような、複数ファイルアップロードもやってみましょう！

ファイル選択
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/249830/69f9bec0-856b-a1dc-362d-043df3ef1537.png)

4ファイル選択結果！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/249830/3612c024-1cb1-3a42-c21a-3d06f27a00a0.png)

Upload！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/249830/eed928b4-a6ad-e28c-599f-1dec6d1f1fa9.png)

出来た！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/249830/bdbb0962-ed1f-923e-b24e-42e117b84e51.png)

それでは、このファイル達をAmazon S3 へファイルアップロードします。


##AWS S3接続へ必要なもの
この辺りは他のサイトがとてもわかりやすく説明してくれているので、参考にさせていただいたリンクを張っていきます。
とても助かりました。ありがとう！

<h3> 1 . AWS アカウント作成</h3>
大前提ですな。作成後に色々と気をつける点があるので、是非設定しましょう。

AWSアカウントを取得したら速攻でやっておくべき初期設定まとめ：
　https://qiita.com/tmknom/items/303db2d1d928db720888

<h3> 2 . AWS S3用のキーを取得する </h3>

awsのs3を操作する為のaccess keyとsecret keyを取得する(IAM)：
　https://joppot.info/2014/06/14/1621

<h3> 3 . Visual StudioにAWS用のプラグインをインストールします </h3>

クラウドストレージ（Amazon S3）に画像をアップロード、参照する：
　https://leadtools.grapecity.com/topics/news-20170628

これで一旦準備はOK！

##Amazon S3アップロードの実装

1. Amazon S3用のインターフェイスオブジェクト作成
　「IAmazonS3 client」の記述を行います。

2. Amazon S3 クライアントの設定
　AmazonS3Clientを作成する際に、アクセスキー及び、地域を引数に設定する必要があります。
　アクセスキーはそれぞれ取得していただきますが、地域はそれぞれ異なると思います。
　今回の場合、米国東部（オハイオ）となっております。
　準備の際に設定したリージョンを設定してください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/249830/28ce6b62-331f-0be5-19a8-e01573ba948c.png)
　その他の地域は以下AWSのサイトを参照し、適切な引数の設定をしてください。
　　https://docs.aws.amazon.com/ja_jp/general/latest/gr/rande.html

3. S3上の保存先を設定
　S3 にバケットを作成します。
　その配下にworkフォルダを作成します。
　以下のように設定し、アップロードを行います。

　バケット：tanatoru-s3
　パスキー：work/[アップロードファイル名]

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/249830/a67a2da7-7a34-df35-c527-f4992b82fcd0.png)



それでは、実装したソースコードはこちらになります。
通常のファイルアップロードも記載されているので、変更点が見たい場合は上記同名のファイルとWinMerge等を使って比較してみてください。

```Controllers\WorkController.cs
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Amazon;
using Amazon.S3;
using Amazon.S3.Transfer;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using sample2_1.Models;
using sample2_1.Models.Const;

namespace sample2_1.Controllers
{
    public class WorkController : Controller
    {
        // 1. Amazon S3用のインターフェイスオブジェクト作成
        private static IAmazonS3 client;

        public IActionResult Index()
        {
            /* ログインされている場合、共通受け渡しデータにユーザ名を入れておく */
            if (User.Identity.IsAuthenticated)
            {
                var user_id = "";
                var user_nm = "";
                // ユーザIDの特定
                foreach (var i in this.User.Identities)
                {
                    foreach (var n in i.Claims)
                    {
                        if (n.Type == "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier")
                        {
                            user_id = n.Value;
                        }
                        if (user_id != "") break;
                    }
                    if (user_id != "") break;
                }

                // ユーザマスタから情報取得
                using (var db = new AppDbContext())
                {
                    // [出力]
                    foreach (var mUser in db.MUsers.Where(m => m.UserId == user_id))
                    {
                        user_nm = mUser.UserNm;
                        break;
                    }
                }

                ViewData["user_nm"] = user_nm;
            }



            return View();
        }

        [HttpPost("UploadFiles")]
        public async Task<IActionResult> FileUpload(List<IFormFile> files)
        {
            // 保存先を取得
            string filePath = @"C:\work\";

            // 2. Amazon S3 クライアントの設定
            // Amazon S3 キーを取得(準備で取得したキーを設定してください。)
            // このキーがバレると、勝手に使われてしまう可能性があるので、お気をつけて
            var accessKey = ConstKey.AWS_KEY;
            var accessSecretKey = ConstKey.AWS_SECRET_KEY;

            // ファイル保存
            long size = files.Sum(f => f.Length);
            foreach (var formFile in files)
            {
                if (formFile.Length <= 0) continue;
                // ローカルに保存
                using (var stream = new FileStream(filePath + formFile.FileName, FileMode.Create))
                {
                    await formFile.CopyToAsync(stream);
                }

                // Amazon S3 クライアントの設定
                client = new AmazonS3Client(accessKey
                                           , accessSecretKey
                                           , RegionEndpoint.USEast2);
                // 非同期メソッドの実行
                WritingAnObjectAsync(formFile.FileName).Wait();


            }

            // 画面に返す返却値
            ViewData["uploadResult"] = Ok(new { count = files.Count, size, filePath }).Value.ToString();

            // ViewをIndex画面で返却
            return View("Index");
        }

        // S3 ストレージへアップロード！
        static async Task WritingAnObjectAsync(string s_upd_file_nm)
        {

            // 3. S3上の保存先を設定
            string S3BucketName = @"tanatoru-s3";
            string S3Key = @"work/";

            // 元のファイル名をそのまま利用
            S3Key += s_upd_file_nm;

            try
            {
                // キーを設定したclientを引数に、アップロードクラスを取得
                var fileTransferUtility = new TransferUtility(client);

                // アップロード処理
                using (var fileToUpload =
                    new FileStream(@"c:\work\" + s_upd_file_nm, FileMode.Open, FileAccess.Read))
                {
                    await fileTransferUtility.UploadAsync(fileToUpload
                                                        ,S3BucketName
                                                        ,S3Key);
                }
                // OK！
                Console.WriteLine("Upload 3 completed");

            }
            catch (AmazonS3Exception e)
            {
                Console.WriteLine(
                        "Error encountered ***. Message:'{0}' when writing an object"
                        , e.Message);
            }
            catch (Exception e)
            {
                Console.WriteLine(
                    "Unknown encountered on server. Message:'{0}' when writing an object"
                    , e.Message);
            }
        }
    }
}
```

ところどころソースはサンプルからそのままですが、まぁ使えると思えます。
また、usingにAWS系はかなり増えているので、お忘れなく。

##デバッグ実行（Amazon S3 ver）

まずは、前回同様アップロード
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/249830/c2f1e7c6-2d17-1a46-56f1-d7562cbe383a.png)

結果はOK
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/249830/7799d6ad-8c7b-28ef-e5fe-1efb55947d55.png)

それではそれぞれのファイルを確認しましょう。
まずはローカル、当然OK。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/249830/fcbab56c-c835-2cfb-b704-871e03d7f6a7.png)

次にAmazon S3、こちらもOK！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/249830/c0d40e22-2960-a16e-97d9-f50bc1b0b962.png)

これでAmazon S3 ストレージへのアップロードが行えました。
本来はダウンロードも書きたいのですが、実はまだTanaToruに未実装なもので、後回しですね。。。
他にサンプルは山ほどあるとは思いますが、コメント頂ければいつか書きます。


##おまけ
あまり必要ないとは思いますが、AWSキーを取得するConstファイルを隠すとこ隠して記載しておきます。

```Models\Const\ConstKey.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace sample2_1.Models.Const
{
    public class ConstKey
    {
        public const String AWS_KEY = @"Your AWS_KEY !";
        public const String AWS_SECRET_KEY = @"Your AWS_SECRET_KEY !";
    }
}

```

このファイルを作っておけばサンプルは作れると思います。