Asp.Net MVC5 でblobへファイルをアップロードする（覚書）

# 概要

少しASP.NET MVCを触ったときに、自分用のメモ
間違ってたり、するかも！！

# 環境

* windows10
* visual studio 2017
* c#

# パッケージ

Visual Studio パッケージ マネージャーから下記コマンドを入力

```
Install-Package WindowsAzure.Storage
```

# アカウント情報について

## 開発環境でblobを使う

「Azure Storage Emulator」があるのでそれを使う。

デフォルトでの接続情報は、次のようになる模様

```
DefaultEndpointsProtocol=http;AccountName=devstoreaccount1;
AccountKey=Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==;
BlobEndpoint=http://127.0.0.1:10000/devstoreaccount1;
TableEndpoint=http://127.0.0.1:10002/devstoreaccount1;
QueueEndpoint=http://127.0.0.1:10001/devstoreaccount1;
```

参考URL: https://docs.microsoft.com/ja-jp/azure/storage/common/storage-use-emulator


### web.confgのappsettingsに書き込む

```web.config
<appSettings>
    <add key="StorageConnectionString" value="ここに接続情報"/>
</appSettings>
```

取り出す

```
 ConfigurationManager.AppSettings["StorageConnectionString"]
```

### web.confgのconnectionStringsに書き込む

```web.config
  <connectionStrings>
    <add name="StorageConnection" connectionString="ここに接続情報"/>
  </connectionStrings>
```

取り出す

```
ConfigurationManager.ConnectionStrings["StorageConnection"].ConnectionString
```

## セキュリティの観点から

ソースへの直書きは、ちょっとやりたくない。
そのため、Azure key valt 等が使えればよいが、今回は使わず簡単に環境変数に設定を行って、そこから取り取り出す形。（Git管理とかで、多くの人にさらすより多少まし）

環境変数値を設定することで、「appSettings」の「connectionStrings」に上書きor追加を行うようにする。
ソースコードとしては、次を参照。
環境変数のキーの頭に「AppSettingsPrefix」の値を付けた、環境変数を設定することで、「appSettings」に上書き、追加される。

「connectionStrings」のほうも同様だが、「sql」の場合には「providerName」が必要となってしまう。そのため、「providerName」が必要なものと不必要なものに分かれている


```app_start/OverrideBootstrapper
using System;
using System.Collections;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Reflection;
using System.Web;

[assembly: PreApplicationStartMethod(typeof(CareMatchingSystem.App_Start.OverrideBootstrapper), "Start")]

namespace CareMatchingSystem.App_Start
{
    public class OverrideBootstrapper
    {
        public static void Start()
        {
            var environmentVariables = Environment.GetEnvironmentVariables()
                                                  .Cast<DictionaryEntry>()
                                                  .ToDictionary(x => (string)x.Key, x => (string)x.Value);

            foreach (var appSetting in environmentVariables.Where(x => x.Key.StartsWith(AppSettingsPrefix, StringComparison.OrdinalIgnoreCase)))
            {
                ConfigurationManager.AppSettings[appSetting.Key.Substring(AppSettingsPrefix.Length)] = appSetting.Value;
            }

            var connectionStrings = new ConnectionStringSettingsCollectionWrapper(ConfigurationManager.ConnectionStrings);

            foreach (var connectionString in environmentVariables.Where(x => x.Key.StartsWith(SqlServerConnStrPrefix, StringComparison.OrdinalIgnoreCase)))
            {
                connectionStrings.AddOrUpdate(connectionString.Key.Substring(SqlServerConnStrPrefix.Length), connectionString.Value, SqlServerProviderName);
            }

            foreach (var connectionString in environmentVariables.Where(x => x.Key.StartsWith(ConnStrPrefix, StringComparison.OrdinalIgnoreCase)))
            {
                connectionStrings.AddOrUpdate(connectionString.Key.Substring(ConnStrPrefix.Length), connectionString.Value);
            }
        }

        private const string AppSettingsPrefix = "APPSETTING_";
        private const string SqlServerConnStrPrefix = "SQLSERVERCONNSTR_";
        private const string ConnStrPrefix = "CONNSTR_";

        private const string SqlServerProviderName = "System.Data.SqlClient";
    }


    internal class ConnectionStringSettingsCollectionWrapper
    {
        public ConnectionStringSettingsCollectionWrapper(ConnectionStringSettingsCollection connectionStrings)
        {
            _connectionStrings = connectionStrings;

            typeof(ConfigurationElementCollection).GetField("bReadOnly", BindingFlags.Instance | BindingFlags.NonPublic)
                                                    .SetValue(_connectionStrings, false);
        }

        private readonly ConnectionStringSettingsCollection _connectionStrings;

        public void AddOrUpdate(string name, string connectionString, string providerName)
        {
            var settings = _connectionStrings[name];

            if (settings == null)
            {
                _connectionStrings.Add(new ConnectionStringSettings
                {
                    Name = name,
                    ConnectionString = connectionString,
                    ProviderName = providerName
                });
            }
            else
            {
                typeof(ConfigurationElement).GetField("_bReadOnly", BindingFlags.Instance | BindingFlags.NonPublic)
                                            .SetValue(settings, false);

                settings.ConnectionString = connectionString;
                settings.ProviderName = providerName;
            }
        }

        public void AddOrUpdate(string name, string connectionString)
        {
            var settings = _connectionStrings[name];

            if (settings == null)
            {
                _connectionStrings.Add(new ConnectionStringSettings
                {
                    Name = name,
                    ConnectionString = connectionString
                });
            }
            else
            {
                typeof(ConfigurationElement).GetField("_bReadOnly", BindingFlags.Instance | BindingFlags.NonPublic)
                                            .SetValue(settings, false);

                settings.ConnectionString = connectionString;
            }
        }
    }


}
```

##blobへのアップロードは次のようにする。

 viewModels.PostedFileはHttpPostedFileBaseになり、Postされたファイルが入っている。

```
        /// <summary>
        /// Blobにアップロード
        /// </summary>
        /// <param name="FileStream">アップロードファイル</param>
        /// <param name="ContainerString">コンテナ名（コンテナ命名規約に注意）</param>
        /// <param name="FileNameAfterUpload">アップロード後のファイル名</param>
        /// <returns></returns>
        public static void BlobUpload(System.IO.Stream FileStream, string ContainerString, string FileNameAfterUpload)
        {

            //アカウント情報を取得
            string storageConnectionString = ConfigurationManager.ConnectionStrings["StorageConnection"].ConnectionString;

            CloudStorageAccount account = CloudStorageAccount.Parse(storageConnectionString);
            CloudBlobClient serviceClient = account.CreateCloudBlobClient();

            // コンテナを新規作成
            var container = serviceClient.GetContainerReference(ContainerString);
            container.CreateIfNotExistsAsync().Wait();

            //アップロード後のファイル名を設定
            CloudBlockBlob blob = container.GetBlockBlobReference(FileNameAfterUpload);

            //ファイルをアップロード
            blob.UploadFromStream(FileStream);


        }
```

### blobダウンロード

```

        /// <summary>
        /// Blobからダウンロード(Byte)
        /// </summary>
        /// <param name="ContainerString">コンテナ名</param>
        /// <param name="DownloadFileName">ダウンロードファイル名（Pathを含む）</param>
        /// <returns></returns>
        public static Byte[] BlobDownloadToByte(string ContainerString, string DownloadFileName)
        {

            //アカウント情報を取得
            string storageConnectionString = ConfigurationManager.ConnectionStrings["StorageConnection"].ConnectionString;

            CloudStorageAccount account = CloudStorageAccount.Parse(storageConnectionString);
            CloudBlobClient serviceClient = account.CreateCloudBlobClient();

            //コンテナをセット
            var container = serviceClient.GetContainerReference(ContainerString);

            //ダウンロードのファイル名をセット
            CloudBlockBlob blockBlobDownload = container.GetBlockBlobReference(DownloadFileName);

            //ファイル情報を取得
            blockBlobDownload.FetchAttributes();

            //データ長に合わせ初期化
            byte[] data = new byte[blockBlobDownload.Properties.Length];

            //byte型で取得
            blockBlobDownload.DownloadToByteArray(data, 0);

            return data;

        }
```