前から Google Cloud Storage には興味があったのでラーメンの写真を記録するサービスを作るのに使ってみました。機能ごとにもリンクを貼りますが、ひとまず公式ドキュメントとリポジトリは以下になります。
実行環境
go: v1.18
cloud.google.com/go/storage: v1.22.0
※ 全体を通してエラーハンドリングは省略しています。
事前準備:クライアントの作成
クライアントの作成に使用するため、事前に GCP のアカウント作成と鍵ファイルをダウンロードしておきます。
今回はダウンロードした鍵ファイルを引数にとり [WithCredentialsFile] を使用します。(https://pkg.go.dev/google.golang.org/api/option#WithCredentialsFile)
// これ以降のコードは func main 内のみ記載します
package main
import (
"context"
"cloud.google.com/go/storage"
)
func main() {
ctx := context.Background()
client, _ := storage.NewClient(ctx, option.WithCredentialsFile("./key.json"))
}
公式ドキュメントには
The client will use your default application credentials. Clients should be reused instead of created as needed. The methods of Client are safe for concurrent use by multiple goroutines.
クライアントは必要に応じて作成するのではなく、再利用することが望ましいです。Clientのメソッドは、複数のゴルーチンによる同時使用にも安全です。
とあるため、本記事では都合上毎度定義し直してますが どこか一箇所で定義して使い回すのがよさそうです。
複数のゴルーチンによる同時使用にも安全です。
とあるのは context を引数としてるからですかね。あまり context を理解していない
バケットの作成
バケットの作成は BucketHandle.Create を使用します。
bucket-name
は作成したいバケット名で、 my-project
はバケットを作りたいプロジェクト名(実在するもの)です。
ctx := context.Background()
client, _ := storage.NewClient(ctx)
client.Bucket("bucket-name").Create(ctx, "my-project", nil)
余談ですが、 client.Project("my-project").CreateBucket(ctx, "bucket-name")
みたいな書き方の方が個人的にはしっくりくるので、できそうでしたら教えてください🙇♂️
ファイルのアップロード
ファイルアップロード(オブジェクトの作成)には BucketHandle.Create を使用します。
// 適当なファイルを作成
f, _ := os.Create("sample.txt")
// file-name はオブジェクトに付けたい名前
writer := user.Client.Bucket("bucket-name").Object("file-name").NewWriter(ctx)
io.Copy(writer, e.File)
writer.Close()
file-name
は folder/file.txt
のようにパス構造にする事で階層を持ってアップロードする事ができます。
※1 file-name
をパス構造として階層にすることはあまりお勧めしません。
詳しくはディレクトリトラバーサルと検索ください。
※2 オブジェクトの更新にObjectHandle.Update がありましたが、BucketHandle.Create が UPSERT (同じ file-name
のオブジェクトなければ作成、あれば更新)のような役割をしていたため今回は使用してません。個人開発なのでオブジェクトのバージョンとかも気にする必要はありませんし。
パフォーマンスなどで明らかに優れる点がありましたら教えてください。
オブジェクトの取得
オブジェクトの取得には ObjectHandle.NewReader を使用します。
obj := user.Client.Bucket("bucket-name").Object("file-name")
rc, _ := obj.NewReader(ctx)
buf := new(bytes.Buffer)
io.Copy(buf, rc);
特定の条件のオブジェクトの情報を取得したい場合は BucketHandle.Objects を使用するようです。
ctx := context.Background()
client, _ := storage.NewClient(ctx)
it := client.Bucket("my-bucket").Objects(ctx, nil)
BucketHandle.Objects
の第二引数へは Query を渡す事ができ、取得するオブジェクトを絞り込めるようです。(nil
でバケット配下を全て取得します)
たとえば folder
配下のオブジェクトの情報(ObjectIterator)のみを取得する場合は以下のようになります。
ctx := context.Background()
client, _ := storage.NewClient(ctx)
query := &storage.Query{
StartOffset: "folder/"
}
it := client.Bucket("my-bucket").Objects(ctx, query)
この ObjectIterator
からそれぞれのオブジェクト名(ObjectAttrs.name を取得して、ObjectHandle.NewReader
を取得します。
(より良いやり方ありましたら教えていただければと思います🙇♂️)
以下の公式ドキュメントも参考にいただければと思います。
オブジェクトの削除
オブジェクトの削除には BucketHandle.Delete を使用します。
ctx := context.Background()
client, _ := storage.NewClient(ctx)
client.Bucket("my-bucket").Delete(ctx)
最後に
今回は基本的な操作に関してのみ記載いたしました。今後気になる機能があったら追記等々行っていきたいと思います。
最後までお付き合いいただきありがとうございました🙇♂️
参考リンク