golang echoでgoogle cloud storageにファイルをuploadしてみる

はじめに

golangフレームワークechoを使用してgoogle cloud storageにファイルをアップロードするAPIを作成してみた。 コードは後ほどgithubにあげる予定

google cloud storageと接続

Google Cloud Platform のサービスアカウントを取得し、gstorage/keysに保存する 今回は.envrcからファイル名を取得している gstorage/gstorage.go

package gstorage

import (
    "context"
    "log"
    "os"

    "github.com/pkg/errors"

    "cloud.google.com/go/storage"
    "google.golang.org/api/option"
)

type googleStorage struct {
    client *storage.Client
    bucket string
}

// NewGoogleStorage mount google storage
func NewGoogleStorage() GoogleStorage {
    ctx := context.Background()
    accountKey := os.Getenv("GCLOUD_ACCOUNT_KEY")
    if accountKey == "" {
        err := errors.New("Could not found google service account key")
        log.Fatal(err)
    }
    client, err := storage.NewClient(ctx, option.WithCredentialsFile("./gstorage/keys/"+accountKey))
    if err != nil {
        log.Fatal(err)
    }
    return &googleStorage{
        client: client,
        bucket: os.Getenv("GCLOUD_STORAGE_BUCKET"),
    }
}

// GoogleStorage google storage interface
type GoogleStorage interface {}

main.goでgstorageの呼び出し main.go

func main() {
    storage := gstorage.NewGoogleStorage()
}

gstorage.goにUploadメソッドを実装

gstorage/gstorage.go

// GoogleStorage google storage interface
type GoogleStorage interface {
    Upload(file multipart.File, fileHeader *multipart.FileHeader, filePath string) (string, error)
}

func (gs *googleStorage) Upload(file multipart.File, fileHeader *multipart.FileHeader, filePath string) (string, error) {
    ctx := context.Background()

    sw := gs.client.Bucket(gs.bucket).Object(filePath + "/" + fileHeader.Filename).NewWriter(ctx)
    if _, err := io.Copy(sw, file); err != nil {
        return "", errors.New("Could not write file: " + err.Error())
    }

    if err := sw.Close(); err != nil {
        return "", errors.New("Could not put file: " + err.Error())
    }

    u, _ := url.Parse("/" + gs.bucket + "/" + sw.Attrs().Name)
    return "https://storage.googleapis.com" + u.EscapedPath(), nil
}

controllerの実装

controller/controller.go

package controller

import (
    "net/http"

    "github.com/labstack/echo"
    "github.com/y-ogura/echo-google-cloud-storage/gstorage"
)

// Controller controller
type Controller struct {
    Storage gstorage.GoogleStorage
}

// NewController mount controller
func NewController(e *echo.Echo, storage gstorage.GoogleStorage) {
    handler := &Controller{
        Storage: storage,
    }

    e.GET("/", handler.InputForm)
    e.POST("/upload", handler.Upload)
}

// InputForm input form
func (c *Controller) InputForm(ctx echo.Context) error {
    return ctx.HTML(http.StatusOK, formHTML)
}

// Upload upload file
func (c *Controller) Upload(ctx echo.Context) error {
    file, fileHeader, err := ctx.Request().FormFile("file")
    if err != nil {
        return status.ResponseError(ctx, err)
    }
    defer file.Close()

    res, err := c.Storage.Upload(file, fileHeader, "filePath")
    if err != nil {
        return ctx.JSON(http.StatusInternalServerError, err)
    }
    return ctx.JSON(http.StatusOK, res)
}

const formHTML = `<!DOCTYPE html>
<html>
  <head>
    <title>Storage</title>
    <meta charset="utf-8">
  </head>
  <body>
    <form method="POST" action="/upload" enctype="multipart/form-data">
      <input type="file" name="file">
      <input type="submit">
    </form>
  </body>
</html>`

main.goでcontrollerを呼び出して環境を起動

main.go

func main() {
    e := echo.New()

    storage := gstorage.NewGoogleStorage()

    controller.NewController(e, storage)

    port := ":" + os.Getenv("PORT")
    e.Logger.Fatal(e.Start(port))
}