ASP.NET で Azure Blob Storage を操作してみる

#Visual Studio で BlobStorage を扱う Web アプリケーションを作成する
BlobStorgae の扱いは Cognitive Services や Data/AI を扱う上で非常に便利になってくるみたいです。
加えて Functions などのサーバーレスコンピューティングとの連携も非常にスムーズ。
これまであまり BlobStorage に触れてこなかったのですが、今回は整理も兼ねて基礎的な部分を整理しておきたいと思います。

参考:Azure Blob Storage と Visual Studio 接続済みサービスの概要 (ASP.NET)
https://docs.microsoft.com/ja-jp/azure/visual-studio/vs-storage-aspnet-getting-started-blobs

今回やることは以下の通り
1. Visual Studio で作成した MVC アプリケーションから　Blob を操作できる環境を構築する
2. 実際にデプロイしたアプリケーションでデータ登録/参照などを実施する

##Visual Studio で作成した MVC アプリケーションから　Blob を操作できる環境を構築する
Docs に書いてある方法は以下の通り。
1. Visual Studio にて、新しいプロジェクトの作成から ASP.NET Web アプリケーション (.NET Framework)を選択する。
2. 作成ダイアログで、Web アプリケーション (モデル ビュー コントローラー) を選択する
3. ソリューションを右クリックから、追加>接続済みのサービスを選択
4. 接続済みのサービスを選択すると、Visual Studio によって Azure Storage 用の NuGet パッケージと appsettings.json へのストレージ接続文字列がインストールされます。
5. MVC コントローラーを作成する (BlobsController という名前を付ける)

ここまでは普通に進むのだけど、ここから先の Storage Connection String を入力するのが、Docs だと分かりにくい！！
Docs の記載は以下の通り。
> "CloudBlobContainer を返す GetCloudBlobContainer と呼ばれるメソッドを追加します。 必ず <storageaccountname>_AzureStorageConnectionString を Web.config 内の実際のキーの名前に置き換えてください。"

記載方法的に、Web.config の中の文字をそのまま使えばよいのだが、それよりも、「Connection String」の値をそのまま<storageaccountname>_AzureStorageConnectionString に置き換えれば良いという話だった。

Visual Studio から Storage Services を接続すると、必要な NugetPackage と web.config は自動的に更新されるのだが、いちいちそれをコピペするのはめんどくさい、、、。なのでここは Azure Portal から直接貼った方が楽かもです。

ここまでくれば、あとはデプロイすればローカル環境から Blob に接続し、新しいコンテナーを作成することができます。

##実際にデプロイしたアプリケーションでデータ登録/参照などを実施する
UplodadBlob() メソッドの中のOnroadの部分は任意のファイルパスに変えなくてはいけない。

```
using (var fileStream = System.IO.File.OpenRead(@"<ここのPath!!>"))
```
あとは基本 Docs 通りに進めば良いが、いまいちど .NET での 参照/作成/アップロード/リスト/ダウンロード/デリートのメソッド詳細について纏めておく・

① 接続 GetCloudBlobContainer() メソッド

```
private CloudBlobContainer GetCloudBlobContainer()
{
    CloudStorageAccount storageAccount = CloudStorageAccount.Parse(
            CloudConfigurationManager.GetSetting("<storageaccountname>_AzureStorageConnectionString"));
    CloudBlobClient blobClient = storageAccount.CreateCloudBlobClient();
    CloudBlobContainer container = blobClient.GetContainerReference("test-blob-container");
    return container;
}
```

② 作成 CreateBlobContiner() メソッド

```
public ActionResult CreateBlobContainer()
{
    CloudBlobContainer container = GetCloudBlobContainer();
    ViewBag.Success = container.CreateIfNotExists();
    ViewBag.BlobContainerName = container.Name;

    return View();
}
```

③ アップロード Upload() メソッド

```
public string UploadBlob()
{
    CloudBlobContainer container = GetCloudBlobContainer();
    CloudBlockBlob blob = container.GetBlockBlobReference("myBlob");
    using (var fileStream = System.IO.File.OpenRead(@"<任意のパス>"))
    {
        blob.UploadFromStream(fileStream);
    }
    return "success!";
}
```

④リスト ListBlobs() メソッド

```
public ActionResult ListBlobs()
{
    CloudBlobContainer container = GetCloudBlobContainer();
    List<string> blobs = new List<string>();
    foreach (IListBlobItem item in container.ListBlobs(useFlatBlobListing: true))
    {
        if (item.GetType() == typeof(CloudBlockBlob))
        {
            CloudBlockBlob blob = (CloudBlockBlob)item;
            blobs.Add(blob.Name);
        }
        else if (item.GetType() == typeof(CloudPageBlob))
        {
            CloudPageBlob blob = (CloudPageBlob)item;
            blobs.Add(blob.Name);
        }
        else if (item.GetType() == typeof(CloudBlobDirectory))
        {
            CloudBlobDirectory dir = (CloudBlobDirectory)item;
            blobs.Add(dir.Uri.ToString());
        }
    }

    return View(blobs);
}
```

⑤ ダウンロード Download() メソッド

```
public string DownloadBlob()
        {
            CloudBlobContainer container = GetCloudBlobContainer();
            CloudBlockBlob blob = container.GetBlockBlobReference("myBlob");
            using (var fileStream = System.IO.File.OpenWrite(@"C:\Users\komiyasa\source\repos\sample.txt"))
            {
                blob.DownloadToStream(fileStream);
            }
            return "success!";
        }
```

⑥ 削除 DeleteBlob() メソッド

```
public string DeleteBlob()
{
    CloudBlobContainer container = GetCloudBlobContainer();
    CloudBlockBlob blob = container.GetBlockBlobReference("myBlob");
    blob.Delete();
    return "success!";
}
```

##最終的に変更したファイル
・BlobController.cs

```
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using Microsoft.Azure;
using Microsoft.WindowsAzure;
using Microsoft.WindowsAzure.Storage;
using Microsoft.WindowsAzure.Storage.Blob;

namespace WebApplication1.Controllers
{
    public class BlobsController : Controller
    {
        // GET: Blobs
        public ActionResult Index()
        {
            return View();
        }

        private CloudBlobContainer GetCloudBlobContainer()
        {
            CloudStorageAccount storageAccount = CloudStorageAccount.Parse(
                    CloudConfigurationManager.GetSetting("<<ここに Connection String を入れる>>"));
            CloudBlobClient blobClient = storageAccount.CreateCloudBlobClient();
            CloudBlobContainer container = blobClient.GetContainerReference("test-blob-container");
            return container;
        }

        public ActionResult CreateBlobContainer()
        {
            CloudBlobContainer container = GetCloudBlobContainer();
            ViewBag.Success = container.CreateIfNotExists();
            ViewBag.BlobContainerName = container.Name;

            return View();
        }

        public string UploadBlob()
        {
            CloudBlobContainer container = GetCloudBlobContainer();
            CloudBlockBlob blob = container.GetBlockBlobReference("myBlob");
            using (var fileStream = System.IO.File.OpenRead(@"C:\Users\komiyasa\source\repos\sample.txt"))
            {
                blob.UploadFromStream(fileStream);
            }
            return "success!";
        }

        public ActionResult ListBlobs()
        {
            CloudBlobContainer container = GetCloudBlobContainer();
            List<string> blobs = new List<string>();
            foreach (IListBlobItem item in container.ListBlobs(useFlatBlobListing: true))
            {
                if (item.GetType() == typeof(CloudBlockBlob))
                {
                    CloudBlockBlob blob = (CloudBlockBlob)item;
                    blobs.Add(blob.Name);
                }
                else if (item.GetType() == typeof(CloudPageBlob))
                {
                    CloudPageBlob blob = (CloudPageBlob)item;
                    blobs.Add(blob.Name);
                }
                else if (item.GetType() == typeof(CloudBlobDirectory))
                {
                    CloudBlobDirectory dir = (CloudBlobDirectory)item;
                    blobs.Add(dir.Uri.ToString());
                }
            }

            return View(blobs);
        }

        public string DownloadBlob()
        {
            CloudBlobContainer container = GetCloudBlobContainer();
            CloudBlockBlob blob = container.GetBlockBlobReference("myBlob");
            using (var fileStream = System.IO.File.OpenWrite(@"C:\Users\komiyasa\source\repos\sample.txt"))
            {
                blob.DownloadToStream(fileStream);
            }
            return "success!";
        }

        public string DeleteBlob()
        {
            CloudBlobContainer container = GetCloudBlobContainer();
            CloudBlockBlob blob = container.GetBlockBlobReference("myBlob");
            blob.Delete();
            return "success!";
        }
    }
}

```

・Views/Shared/_Layout.schtml

```
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>@ViewBag.Title - マイ ASP.NET アプリケーション</title>
    @Styles.Render("~/Content/css")
    @Scripts.Render("~/bundles/modernizr")
</head>
<body>
    <div class="navbar navbar-inverse navbar-fixed-top">
        <div class="container">
            <div class="navbar-header">
                <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                </button>
                @Html.ActionLink("アプリケーション名", "Index", "Home", new { area = "" }, new { @class = "navbar-brand" })
            </div>
            <div class="navbar-collapse collapse">
                <ul class="nav navbar-nav">
                    <li>@Html.ActionLink("ホーム", "Index", "Home")</li>
                    <li>@Html.ActionLink("詳細", "About", "Home")</li>
                    <li>@Html.ActionLink("問い合わせ", "Contact", "Home")</li>
                    <li>@Html.ActionLink("Create blob container", "CreateBlobContainer", "Blobs")</li>
                    <li>@Html.ActionLink("Upload blob", "UploadBlob", "Blobs")</li>
                    <li>@Html.ActionLink("List blobs", "ListBlobs", "Blobs")</li>
                    <li>@Html.ActionLink("Download blob", "DownloadBlob", "Blobs")</li>
                    <li>@Html.ActionLink("Delete blob", "DeleteBlob", "Blobs")</li>


                </ul>
            </div>
        </div>
    </div>
    <div class="container body-content">
        @RenderBody()
        <hr />
        <footer>
            <p>&copy; @DateTime.Now.Year - マイ ASP.NET アプリケーション</p>
        </footer>
    </div>

    @Scripts.Render("~/bundles/jquery")
    @Scripts.Render("~/bundles/bootstrap")
    @RenderSection("scripts", required: false)
</body>
</html>

```

<成功イメージ>
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/149921/18d64cf9-0f31-d8a8-96c0-dbea733d648b.png)