.NET Core のアプリから Google Cloud Storage にアクセスする方法

# はじめに
「.NET Core で作成しているWebアプリケーションから、Google Cloud Storage（以下、GCS） にファイルを保存したい」という要件が出てきたときに調べた内容をまとめます。
主な目的はアップロードとダウンロードです。

# 開発環境
* Windows 10
* Visual Studio 2017
* .NET Core 1.1（諸般の事情で2.x系が使えず...）

# 準備
1. GCS でバケットを作成しておく（バケットの名前はあとで使います）
2. APIの使用を許可するため、[Getting Started with Authentication](https://cloud.google.com/docs/authentication/getting-started?hl=ja) の [Creating a service account](https://cloud.google.com/docs/authentication/getting-started?hl=ja#creating_a_service_account) と [Setting the environment variable](https://cloud.google.com/docs/authentication/getting-started?hl=ja#setting_the_environment_variable) を参考に、認証を通しておく
3. nuget から [Google.Cloud.Storage.V1](https://www.nuget.org/packages/Google.Cloud.Storage.V1/)をプロジェクトにインストールする。今回使用したバージョンは 2.1.0-alpha02 です。   


# バケット情報の取得
[Verifying authentication](https://cloud.google.com/docs/authentication/getting-started?hl=ja#verifying_authentication) では、認証が成功しているかどうかのチェックのために行われています。

```csharp:バケット情報の取得
var storage = StorageClient.Create();
var buckets = storage.ListBuckets(projectId);
foreach (var bucket in buckets)
{
    Console.WriteLine(bucket.Name);
}
```

# ファイルのアップロード
ファイルのアップロード・ダウンロードは Utility として使いたかったので、static メソッドとして定義しました。

```csharp:ファイルのアップロード
/// <summary>
/// ファイルを Google Cloud Storage に保存する
/// </summary>
/// <param name="file">アップロードするファイル</param>
/// <param name="bucketName">バケット名/param>
/// <param name="path">保存先のディレクトリパス</param>
/// <param name="fileName">保存するファイル名</param>
/// <returns></returns>
public static bool SaveFileToGcs(IFormFile file, string bucketName, string path, string fileName)
{
    try
    {
        // Google Storage
        var storage = StorageClient.Create();

        using (var memoryStream = new MemoryStream())
        {
            // ファイルデータを stream にコピー
            file.CopyTo(memoryStream);

            // ストレージにアップロード アップロード先のバケットに path/fileName で保存されます
            storage.UploadObject(bucketName, path + "/" + fileName, file.ContentType,
                memoryStream);
        }
    }
    catch (Exception e)
    {
        Console.WriteLine(e);
        return false;
    }

    return true;
}
```


# ファイルのダウンロード
ファイルのダウンロードはユーザーに保存のアクションをさせたかったので、 返戻値が FileStreamResult 形式になっています。

```csharp:ファイルのダウンロード
/// <summary>
/// ファイルを Google Cloud Storage からダウンロードしてクライアントに保存させるActionResultを返す
/// </summary>
/// <param name="bucketName">バケット名</param>
/// <param name="targetFileName">保存したファイル名（パス含）</param>
/// <param name="contentType">コンテンツタイプ</param>
/// <returns></returns>
public static FileStreamResult DownloadFileFromGcs(string bucketName, string targetFileName, string contentType)
{
    try
    {
        var storage = StorageClient.Create();
        var memory = new MemoryStream();

        // GCS からダウンロード
        storage.DownloadObject(bucketName, targetFileName, memory);
        memory.Seek(0, SeekOrigin.Begin);

        return new FileStreamResult(memory, contentType);
    }
    catch (Exception e)
    {
        Console.WriteLine(e);
        return null;
    }

}
```

# おまけ：文字列をファイルにして GCS に保存する
jsonを文字列として渡されて、GCSに保存してほしい！という要件も追加できたので、文字列からファイルを作ってそのまま GCS に保存するメソッドも追加しました。

```csharp:文字列から保存
/// <summary>
/// 文字列を指定されたファイル名で Google Cloud Storage に保存する
/// </summary>
/// <param name="content">ファイルとして保存する内容</param>
/// <param name="bucketName">バケット名</param>
/// <param name="path">保存先のパス</param>
/// <param name="fileName">保存ファイル名</param>
/// <param name="contentType">コンテンツタイプ</param>
/// <param name="encoding">エンコード</param>
/// <returns></returns>
public static bool SaveFileToGcs(string content, string bucketName, string path, string fileName, string contentType, Encoding encoding)
{
    try
    {
        // Google Storage
        var storage = StorageClient.Create();
        
        using (var memoryStream = new MemoryStream())
        using (var writer = new StreamWriter(memoryStream, Encoding.UTF8))
        {
            writer.WriteAsync(content);

            writer.Flush();
            memoryStream.Seek(0, SeekOrigin.Begin);

            // ストレージにアップロード
            storage.UploadObject(bucketName, path + "/" + fileName, contentType, memoryStream);
        }

    }
    catch (Exception e)
    {
        Debug.Fail(e.Message);
        return false;
    }

    return true;
}
```

# さいごに
nuget でパッケージをいれれば特に面倒な設定とかもなく、さくっと使えて、思ってたより簡単にできました。
GCPで.NET Core で、ていうのはまだ記事は少ないような感じがします。特に日本語。
でも、どちらも公式のドキュメントがしっかりしていて、非常にやりやすいです。設計も使い勝手もわかりやすい！

さて、これからも GCP と .NET Core をどんどん使っていきますよー！


# 参考
* [Getting Started with Authentication](https://cloud.google.com/docs/authentication/getting-started?hl=ja)
* [クラウドストレージ（Google Cloud Storage）に画像をアップロード、参照する](https://leadtools.grapecity.com/topics/news-20170726)
* [Google.Cloud.Storage.V1 APIリファレンス Class StorageClient](https://googlecloudplatform.github.io/google-cloud-dotnet/docs/Google.Cloud.Storage.V1/api/Google.Cloud.Storage.V1.StorageClient.html)