GoでGCSのファイルのアップロード＆移動

GCSにファイルをアップロードをしたり、移動したりするのをGolangでやってみました。
レポジトリはこちらに置いてあります。dockerコンテナを立てて試すことができます。
https://github.com/greenteabiscuit/gcs-golang

## 前提条件

- GCPのアカウントがある
- Dockerのダウンロードができている
- GCSの閲覧、編集権限のある鍵ファイルをダウンロードできている

## やること

1. 空のファイル`sample.txt`をGCSにアップロードしてみる
2. GCSにアップロードできた`sample.txt`を別のディレクトリに移動する

## 1. アップロードしてみる

```main.go
package main

import (
	"context"
	"io"
	"log"
	"os"

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

func main() {
	credentialFilePath := "./key.json" // key.jsonはサービスアカウントを作成してゲットする

	// クライアントを作成する
	ctx := context.Background()
	client, err := storage.NewClient(ctx, option.WithCredentialsFile(credentialFilePath))
	if err != nil {
		log.Fatal(err)
	}

	// GCSオブジェクトを書き込む空のsample.txtファイルをローカルで作成
	f, err := os.Create("sample.txt")
	if err != nil {
		log.Fatal(err)
	}

	// オブジェクトのReaderを作成
	bucketName := "experimental-bucket-tt"   // e.g. example-bucket
	objectPath := "sample-object/sample.txt" // e.g. foo/var/sample.txt

	writer := client.Bucket(bucketName).Object(objectPath).NewWriter(ctx)
	if _, err := io.Copy(writer, f); err != nil {
		panic(err)
	}

	if err := writer.Close(); err != nil {
		panic(err)
	}

	log.Println("done")
}
```

Dockerで仮想環境を立てて、その中で実行してみます。
モジュールをダウンロードするのを忘れずに。

```
$ docker-compose up

// In different tab
$ docker exec -it containername bash

//以下のモジュールをダウンロードする必要がある。

/go/src# go mod init example.com/gcs/write

/go/src# go get -u cloud.google.com/go/storage
```

うまくいけばdoneと出るはずです。

```
/go/src# go run main.go
2020/12/29 01:36:24 done
```

バケットのルートディレクトリにてsample.txtがアップロードされているはずです。

![Screen Shot 2020-12-29 at 20.14.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/396623/2730f8e6-e053-c570-35d6-e9e2e07208ca.png)


## 2. ファイルをGCS内で移動する。

残念ながらただの移動する関数はないので、目的のフォルダにコピーし、元のフォルダにあるファイルを消す、という順序になります。
また、途中のフォルダは作成されていない場合はGCS側（？）が作成してくれます。
今回は`destination-folder/30`というテキトーな名前のフォルダに移します。


```main.go
package main

import (
	"context"
	"io"
	"log"
	"os"

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

func main() {
	credentialFilePath := "./key.json" // key.jsonはサービスアカウントを作成してゲットする

	// クライアントを作成する
	ctx := context.Background()
	client, err := storage.NewClient(ctx, option.WithCredentialsFile(credentialFilePath))
	if err != nil {
		log.Fatal(err)
	}

	// GCSオブジェクトを書き込むファイルの作成
	f, err := os.Create("sample.txt")
	if err != nil {
		log.Fatal(err)
	}

	// ......................
	// オブジェクトのアップロード
	// ......................
	bucketName := "experimental-bucket-tt"   // e.g. example-bucket
	objectPath := "sample-object/sample.txt" // e.g. foo/var/sample.txt

	uploadWriter := client.Bucket(bucketName).Object(objectPath).NewWriter(ctx)
	if _, err := io.Copy(uploadWriter, f); err != nil {
		panic(err)
	}

	if err := uploadWriter.Close(); err != nil {
		panic(err)
	}
	log.Println("create file: done")

	// ......................
	// オブジェクトの移動
	// ......................
	dstObjectPath := "destination-folder/30/sample.txt"
	src := client.Bucket(bucketName).Object(objectPath)
	dst := client.Bucket(bucketName).Object(dstObjectPath)
	if _, err := dst.CopierFrom(src).Run(ctx); err != nil {
		panic(err)
	}
	if err := src.Delete(ctx); err != nil {
		panic(err)
	}
	log.Println("move file: done")
}
```

これでファイルを作成してアップロード、そしてアップロードしたファイルの移動に成功するはずです。

```
/go/src# go run main.go
2020/12/29 10:05:55 create file: done
2020/12/29 10:05:55 move file: done
```

`destination-folder/30`フォルダに移動されているはずです。

![Screen Shot 2020-12-29 at 20.13.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/396623/5aaf9dd2-12ed-8df3-8d95-a1ff0460c490.png)



## 参考

> [Reading and Writing to Google Cloud Storage](https://cloud.google.com/appengine/docs/standard/go111/googlecloudstorageclient/read-write-to-cloud-storage)
> [Go言語(golang)でGoogle Cloud Storageへファイルをアップロードする](https://dev.classmethod.jp/articles/golang-write-gcs/)