概要
Go言語でGCS(Google Cloud Storage )のデータを操作する際に、ファイルのアップロード、取得、削除といった処理で共通部分があります。
共通部分をメソッド化して使いまわしたかったのですが、公式のドキュメントだとdeferを使っており、どのようにメソッドにしたらよいかわからなかったので、自分なりに調べて考えた結果を残しておきます。
やりたいこと
GCPに対する操作について公式のドキュメントをみてみると、以下の部分は共通化できそうです...
// bucket := "bucket-name"
// object := "object-name"
ctx := context.Background()
client, err := storage.NewClient(ctx)
if err != nil {
return fmt.Errorf("storage.NewClient: %w", err)
}
defer client.Close()
ctx, cancel := context.WithTimeout(ctx, time.Second*50)
defer cancel()
o := client.Bucket(bucket).Object(object)
今回作成しているアプリでは同じバケットを使っているので、バケットの取得まで共通化して、各々の関数でオブジェクトの取得からやってもらうようにしたいです!
問題点
そのままメソッド化しても、defer
はメソッドがGetCloudStorageBucket
が終わるタイミングで実行されてしまうので、呼び出し元では戻り値のバケットは使えません...
※contextは呼び出し元のものをポインターで渡して使うように変更しています。
func GetCloudStorageBucket(ctx *context.Context) (*storage.BucketHandle, error) {
// bucket := "bucket-name"
// object := "object-name"
client, err := storage.NewClient(*ctx)
if err != nil {
return nil, fmt.Errorf("storage.NewClient: %w", err)
}
defer client.Close()
var cancel context.CancelFunc
*ctx, cancel = context.WithTimeout(*ctx, time.Second*50)
defer cancel()
bucket := client.Bucket(bucketName)
if err != nil {
return nil, fmt.Errorf("client.Bucket: %w", err)
}
return bucket, nil
}
なんとかしてこのdeferを上手い具合に呼び出し元で実行させるようにしたい...
結論 : 戻り値にdeferで実行したい関数を渡してあげる
いくつか調べていると、自分と似たような質問をされている方がいらっしゃいました。
回答者の方はdeferで実行したい関数を変数に詰めていて、これならできそうだったのでやってみました!
func GetCloudStorageBucket(ctx *context.Context) (*storage.BucketHandle, func(), error) {
// bucket := "bucket-name"
// object := "object-name"
client, err := storage.NewClient(*ctx)
if err != nil {
return nil, func(){}, fmt.Errorf("storage.NewClient: %w", err)
}
defer client.Close()
var cancel context.CancelFunc
*ctx, cancel = context.WithTimeout(*ctx, time.Second*50)
defer cancel()
deferFunc := func() {
client.Close()
cancel()
}
bucket := client.Bucket(bucketName)
if err != nil {
return nil, func(){}, fmt.Errorf("client.Bucket: %w", err)
}
return bucket, deferFunc, nil
呼び出しもと
ctx := context.Background()
bucket, clientDeferFunc, err := GetCloudStorageBucket(&ctx)
if err != nil {
return err
}
defer clientDeferFunc()
objectName := "object-name"
obj := bucket.Object(objectName)
問題なくGCSに対して操作することができました!🙌
まとめ
Go言語に触れたばかりで全然慣れていないので、初歩的な部分で悩んでしまいました🥲
ですがこれを機にGoについて少し理解できた気がします!
もしもっとよい方法などあれば教えていただけますと大変嬉しいです!🥳
参考