© 2021 Addapter Inc.

BLOG

Cloud Runを使って複数の画像をアップロードしたい【Golang】

コーディング2022/01/21
blog author
なーこ
blog thumbnail

目次

目標

「あるエンドポイントに複数の画像ファイルをデータとしてリクエストすると、GCP Storageにその画像がアップロードされ、各画像のファイル名とファイルサイズがJSONとして返ってくること」

今回使うもの

  • GCP Storage (ファイルとか保存するやつ)
  • Secret Manager (見られたくないファイルとか値を入れておくやつ)
  • Cloud Run (コンテナをデプロイしていい感じに整備してくれるやつ)

各ステップ

Step1 ファイルをアップロードするコードをかく(golang)

今回はfiberというフレームワークを使って、コードを書いていきます。

以下は、/artsにアクセスすると、RegisterImageという関数が実行されるというコードです。

main.go

関数RegisterImageでは「①リクエストされた一枚以上の画像ファイルを受け取り、②gcp Storageへのアップロードが完了した後に、③その各画像ファイルのファイル名とファイルサイズをJSONで返す」ということをやっています。

routes/upload_Image.goのファイルについて解説してみます。

package routes

import (
	"cloud.google.com/go/storage"
	"context"
	"fmt"
	"github.com/gofiber/fiber/v2"
	"google.golang.org/api/option"
	"io"
	"log"
)

type FinalFiles struct {
	Filename  string `json:"filename"`
	Filesize int64  `json:"filesize"`
}

func RegisterImage(c *fiber.Ctx) error {
	f, err := c.MultipartForm()
	log.Print(f)
	if err != nil {
		return c.SendStatus(fiber.StatusNotFound)
	}

	blobFile:= f.File

	ctx := context.Background()
	storageClient, Eerr := storage.NewClient(ctx, option.WithCredentialsFile("/secrets/key.json"))
	if Eerr != nil {
		log.Print(Eerr)
		return c.SendStatus(fiber.StatusUnauthorized)
	}

	bucketName := "works_arts"
	var finals []*FinalFiles

	for _, value := range blobFile {
		file, err := value[0].Open()
		stream := &FinalFiles{
		  Filename:  value[0].Filename,
		  Filesize :value[0].Size,
		}
		if err != nil {
			return c.SendStatus(fiber.StatusNotFound)
		}
		wc := storageClient.Bucket(bucketName).Object(value[0].Filename).NewWriter(ctx)

		if _, err := io.Copy(wc, file); err != nil {
			return fmt.Errorf("io.Copy: %v", err)
		}
		if err := wc.Close(); err != nil {
			return fmt.Errorf("Writer.Close: %v", err)
		}
		finals = append( finals, stream)
	}

	return c.JSON(finals)
}

解説

Fiberでは複数のバイナリファイルを受け取るコンテキストとして、MultipartFormという関数を提供しています。これを用いることで、複数の画像ファイルを受け取り処理をすることができます。

GCP Storageの処理です。クライアント(NewClient)をサービスアカウントの認証情報ファイルを指定して建てます。

Rangeで受け取ったファイルを一つずつ回しながら、指定したバケット(bucketName)にファイル名をオブジェクトとしてfinalsという変数に次々と書き込んでいきます。

最後にfinalsをJSONで返します。

————————————

Step2  コンテナ化する

Dockerfileの中身です。

マルチステージビルドを利用しており、あらかじめパッケージをダウンロードしておくことで、イメージサイズを小さくすることができます。

Step3  Google Container Registryにイメージをビルド・プッシュ

以下のコマンドでイメージをビルド、プッシュします。

 gcloud builds submit —project プロジェクト名 --tag gcr.io/プロジェクト名/register-image 

Step4  権限とCloud Runでの設定

GCP Storageのクライアントを立てるときに認証情報ファイルが必要になります。今回はSecret Managerを用いて実装したいと思います。

まず、GCPのサービスアカウントを作成します。作成したら、サービスアカウントの「キー」タブにて、鍵を追加ボタンを押して、

「新しい鍵を作成」からキーのタイプを「JSON」にして鍵を発行します。JSONファイルがダウンロードされるので、これを用いてシークレットを作ります。

Secrets Managerを開いて、シークレットを作成をクリック。ファイルをアップロードから先ほどのJSONファイルを選択。シークレット作成を押すと、完了です。

Gcp Storageにバケットを作成します。この時、権限タブで先ほど作成したサービスアカウントに対して、「Storage オブジェクト作成者」の権限を付与しておきます。

Cloud Runのサービスを作成します。

cloud Runのサービスの作成をクリックし、「既存のコンテナイメージから一つのリビジョンをデプロイする」の選択を押して、

先ほどプッシュしたイメージを選択します。全てのトラフィックを許可、未承認の呼び出しを許可し、公開APIとします。

Cloud Runコンソールに行き、サービスの作成をクリック

既存のコンテナイメージから一つのリビジョンをデプロイするの選択をクリックするとContainer registryのコンテナ一覧が出ます。

ここから先ほど作成したイメージを選択します。認証については今回は未認証の呼び出しを許可にしておきます。

コンテナ、変数とシークレット、接続、セキュリティタブをクリックし、変数とシークレットタブからシークレットの参照をクリックします。

gcp storageにアクセスするサービスアカウントの認証情報ファイルをボリュームとしてマウントします。

シークレット

ファイルに先ほど作成したシークレットを指定します。

ボリュームとしてマウントを参照方法とし、マウントパスとパスをgcp storageのクライアントを立てた時に指定したパスになるよう指定します。

ここでは先ほど、

storageClient, Eerr := storage.NewClient(ctx, option.WithCredentialsFile(“/secrets/key.json”))

のように書いたので画像のように指定しました。

作成ボタンをクリックして、サービスを作成します。

作成が完了すると、緑のチェックマークが付き、エンドポイントが発行されます。

Step5 Postmanで実行してみる

指定のエンドポイントにファイルを3つリクエストを送ってみると、無事ファイル名とファイルサイズの一覧が返ってきました。

以上です!

scroll to Top