より良いエンジニアを目指して

1日1つ。良くなる!上手くなる!

ASP.NET Core MVC + Google App EngineでGoogle Cloud Storageに保存してみる

GCS(Google Cloud Storage)への保存は、公式ドキュメントを見れば良いのですが、躓きました。

とりあえずローカル保存を試してみたが、エラー

f:id:rimever:20190917205143p:plain

System.InvalidOperationException
  HResult=0x80131509
  Message=The Application Default Credentials are not available. They are available if running in Google Compute Engine. Otherwise, the environment variable GOOGLE_APPLICATION_CREDENTIALS must be defined pointing to a file defining the credentials. See https://developers.google.com/accounts/docs/application-default-credentials for more information.
  Source=Google.Apis.Auth
  スタック トレース:
   at Google.Apis.Auth.OAuth2.DefaultCredentialProvider.<CreateDefaultCredentialAsync>d__11.MoveNext()
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Google.Api.Gax.Rest.ScopedCredentialProvider.<CreateDefaultCredentialsUncached>d__5.MoveNext()
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at Google.Api.Gax.TaskExtensions.WaitWithUnwrappedExceptions(Task task)
   at Google.Cloud.Storage.V1.StorageClient.Create(GoogleCredential credential, EncryptionKey encryptionKey)
   at GoogleCloudSamples.StorageQuickstart.Main(String[] args) in 

Google Compute Engineで動かすにはGOOGLE_APPLICATION_CREDENTIALの環境変数の設定が必要です。

ASP.NET Core + Google App Engine

ですが、Google App Engineの場合は、環境変数の設定が必要ありません。

今回はASP.NET CoreにWebサービスにアップロードされたファイルをGoogle Cloud Storageに保存するプログラムを書いてみました。

@{
    ViewData["Title"] = "Home Page";
}
@using (Html.BeginForm(
"Index",      // アクション名
"Share",    // コントローラー名
FormMethod.Post,                               // HTTP メソッド
new { enctype = "multipart/form-data" } // その他の属性
))
{
    <div class="form-group">
        <div class="col-md-10">
            <p>Upload one or more files using this form:</p>
            <input type="file" name="files">
        </div>
    </div>
    <div class="form-group">
        <div class="col-md-10">
            <input type="submit" value="Upload">
        </div>
    </div>
}
  1. IFormFileをインターフェースとして受け取る
  2. 受け取ったファイルを一時ファイルとして保存
  3. 一時ファイルをGCSのバゲットに保存
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Google.Cloud.Storage.V1;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;

namespace OTRSPlusCloud.Controllers
{
    public class ShareController : Controller
    {
        private const string BucketName = "自分で宣言してね";

        [HttpPost]
        public async Task<IActionResult> Index(List<IFormFile> files)
        {
            long size = files.Sum(f => f.Length);

            // full path to file in temp location
            var filePath = Path.GetTempFileName();

            foreach (var formFile in files)
            {
                if (formFile.Length > 0)
                {
                    using (var stream = new FileStream(filePath, FileMode.Create))
                    {
                        await formFile.CopyToAsync(stream);
                    }
                    UploadFile(BucketName, filePath, formFile.FileName);
                }
            }

            // process uploaded files
            // Don't rely on or trust the FileName property without validation.

            return Ok(new { count = files.Count, size, filePath });
        }
        private void UploadFile(string bucketName, string localPath,  string objectName = null)
        {
            var storage = StorageClient.Create();
            using (var f = System.IO.File.OpenRead(localPath))
            {
                objectName = objectName ?? Path.GetFileName(localPath);
                storage.UploadObject(bucketName, objectName, null, f);
                Console.WriteLine($"Uploaded {objectName}.");
            }
        }
    }
}

f:id:rimever:20190919081301p:plain
ファイルをアップロードします。

f:id:rimever:20190919081211p:plain
成功した。

f:id:rimever:20190919080850p:plain
Google Cloud Storageのバゲットにも保存された。

サイズの大きなデータには

さすがにこの方法ではサイズが大きいデータは扱えません。

その場合はGCSへの署名付きURLを発行することになります。

方法は後日書きます。