.NET Framework 4.5.2でAzure Blob Storageを利用する

#はじめに
C#からAzure Blob Storageを利用する記事を探すと、v12のAPIを提供するNuGetパッケージ(Azure Storage Blobsなど)を利用するサンプルが多く表示されるが、v12は.NETStandar 2.0以上をターゲットしているため、.NET Framework 4.5.2からは利用することができません。
BtoB製品の場合、導入先のお客様が現在運用している可能性のあるWindows Server全てに対応する必要があるため、現在利用可能な.NET Frameworkの最低バージョン4.5.2(それ以前はMSのサポート対象外)をターゲットと考えなければなりません。
「Azure Web Appsで運用するのだったら4.7以降をターゲットとすればいいのでは？」と考えるかもしれませんが、オンプレとWeb Appsで同じ製品のターゲットフレームワークを変えるというのも構成管理やテストのコストがかかる場合があるのでできれば避けたいところです。
(※.NETStandardの実装とサポートする.NET Frameworkのバージョンに関しては、MSページ(https://docs.microsoft.com/ja-jp/dotnet/standard/net-standard)　を参考にしてください。)
本記事ではそのような制約下の開発でAuzre Blob Storageを利用するには、どのNuGetパッケージを用いて、どのようなコーディングを行えばいいのかを見ていこうと思います。

#利用するNuGetパッケージ
Azure Blob Storageを操作するためのパッケージはいくつか公開されていますが、.NET Framework 4.5.2で利用可能で、かつ2020年10月時点でサポート切れが見込まれていないものは以下のパッケージになります。
・Microsoft.Azure.Storage.Blob

#Blob Storageにファイルをアップロードする
Azure Storage Blobを利用するには、Blobやファイルなどの各種ストレージオブジェクトへのアクセスを提供するストレージアカウントというサービスの単位があり、ストレージアカウントに対して認証を受けたうえで、ストーレージアカウントの管理する各種ストレージにアクセスをするという流れになります。
Blobに対してのアクセスは以下のようなコードになります

```c#:Upload.ashx.cs
using System.Configuration;
using Microsoft.Azure.Storage;
using Microsoft.Azure.Storage.Blob;

// Web Appsの場合、接続文字列はApplication Settingsから取るのがおそらく一般的。
string connectionstring = ConfigurationManager.AppSettings["connectionstring"];
// blobが保存されるコンテナ名
string containername = ConfigurationManager.AppSettings["containername"];

// アップロードされたファイルを取得
HttpPostedFile file = HttpContext.Current.Request.Files["userfile"];
if (!string.IsNullOrEmpty(file.FileName))
{
    CloudStorageAccount storageAccount = CloudStorageAccount.Parse(connectionstring);
    CloudBlobClient client = storageAccount.CreateCloudBlobClient();

    CloudBlobContainer container = client.GetContainerReference(containername);
    // もしコンテナが存在しなかった場合は作成
    container.CreateIfNotExists();

    // 一意となるファイル名でblobに対しての参照を取得する
    CloudBlockBlob blob = container.GetBlockBlobReference($"{file.FileName}");

    using (var fileStream = file.InputStream)
    {
        // ファイルをアップロードする
        blob.UploadFromStream(fileStream);
    }
}
```

#Blobからファイルをダウンロードする
ダウンロードの場合は、Responseに対してのファイルの書き出し方によっていくつか利用するメソッドが異なりますが、本記事ではbyteの配列として書き出すやり方を採用します。

```c#:Download.ashx.cs
using System.Configuration;
using Microsoft.Azure.Storage;
using Microsoft.Azure.Storage.Blob;

// Web Appsの場合、接続文字列はApplication Settingsから取るのがおそらく一般的。
string connectionstring = ConfigurationManager.AppSettings["connectionstring"];
// blobが保存されるコンテナ名
string containername = ConfigurationManager.AppSettings["containername"];
// ダウンロードするファイル名
string filename = HttpContext.Current.Request["filename"];
if (filename != null)
{
    CloudStorageAccount storageAccount = CloudStorageAccount.Parse(connectionstring);
    CloudBlobClient client = storageAccount.CreateCloudBlobClient();
    CloudBlobContainer container = client.GetContainerReference(containername );
    CloudBlockBlob blob = container.GetBlockBlobReference(fileName);

    // 必要なbyte配列の長さを取得するためには、取得したblobの参照に対してFetchAttributesを行う必要がある
    blob.FetchAttributes();
    long fileByteLength = blob.Properties.Length;
    byte[] filecontent = new byte[fileByteLength];
    blob.DownloadToByteArray(filecontent, 0);
    if (filecontent.Length != 0)
    {
        // ここでもちいてるcontextは利用しているクラス(HandlerとかPageとか)に応じて適当なものを利用してください
        context.Response.BinaryWrite(filecontent);
    }
    context.Response.Flush();
}
```

#結論
上記の通り、比較的簡単なコードでBlob Storageを利用することができた。
ネット上の資料だと、FetchAttributesをせずにProperties.Lengthを取得している記事が見つかるが、私の環境ではFetchAttributesを行わないとLength=-1が返ってきてしまいbyte配列の宣言時にエラーが返ってきてしまった。
これが単純なネット記事の間違いなのか、なにかしらの環境に依存する際なのかは不明だが、とりあえずはこのコーディングで動いています。