Go で Azure Storage Blobを扱うときの覚え書き
azblobに関する、備忘録代わりの記事です。(古い記事やパッケージなどが検索に紛れてきてしまうので)
SDKのリポジトリは以下にあります。執筆時点で v1.0.0 が最新です(ちょうど執筆前にリリースされたようです) 以前のバージョン(v0.5 とか v0.6)を使っている人はアップデートするとよいでしょう。
- https://github.com/Azure/azure-sdk-for-go/tree/main/sdk/storage/azblob#readme
- https://github.com/Azure/azure-sdk-for-go/releases/tag/sdk%2Fstorage%2Fazblob%2Fv1.0.0
パッケージは以下にあります。
azblob
と検索すると、古いバージョンが先にヒットしてしまう場合がありあますが、理由がなければ使わないようにしましょう。
一般的な使い方
他のSDKでよくある使い方、例えば
- 接続文字列でクライアントを作る
- コンテナオブジェクトを作り、コンテナを作成する(CreateIfNotExistとか)
- BLOBオブジェクトを作り、アップロード・ダウンロードする
みたいなシナリオをGoで書いてみます。
初期設定
初期化してSDKを取得しておきます。
go mod init main
go get github.com/Azure/azure-sdk-for-go/sdk/storage/azblob
接続文字列外のクレデンシャルを使う場合は、azidentity も必要です。
go get github.com/Azure/azure-sdk-for-go/sdk/azidentity
接続からコンテナの作成まで
接続文字列からクライアントを作成して、コンテナを作る簡単なコード例です。AZURE_STORAGE_CONNECTION_STRING
に接続文字列を設定しておきます。
特に難しいところないのですが、コンテナが既に存在するとエラーが発生するので、自分でハンドルするしか無さそうです。どうやってハンドルするのか分からなかったので、ソースをつらつら参照していたら、bloberror.HasCode
という関数があったので使ってみましたが、一応これで判断が可能ですが、正しい方法か分かりません。
パッケージの説明には、
| CreateContainer is a lifecycle method to creates a new container under the specified account. If the container with the same name already exists, a ResourceExistsError will be raised. This method returns a client with which to interact with the newly created container.
と、ResourceExistsError
なるものがRaiseされると書かれていましたが、ソースをググってもそれに該当しそうなコードは見つかりませんでした。なんとなく他の言語のSDKの説明なのではないかという感じです(Pythonぽい)。
ctx := context.Background()
connectionString, ok := os.LookupEnv("AZURE_STORAGE_CONNECTION_STRING")
if !ok {
log.Fatal("'AZURE_STORAGE_CONNECTION_STRING' not found")
}
// 接続文字列でクライアントを作成する
serviceClient, err := azblob.NewClientFromConnectionString(connectionString, nil)
if err != nil {
panic(err)
}
// コンテナの作成
r, err := serviceClient.CreateContainer(ctx, "test-container", &azblob.CreateContainerOptions{})
if err != nil {
if bloberror.HasCode(err, bloberror.ContainerAlreadyExists) {
// コンテナが既に存在する
fmt.Printf("container already exists")
} else {
panic(err)
}
}
fmt.Printf("%v\n", r)
ファイルのアップロード
他のSDKだとコンテナを作成すると、コンテナ用のクライアントオブジェクトが取得できて、それに対してBlobClientを作成してアップロードみたいな感じですが、GoではすべてserviceClient
で処理します
アップロードする関数はいくつかありますが、以下はFileからの例です。特にオプションを指定しないと、Blobは常に上書きされます。
f, err := os.Open("./sample.txt")
if err != nil {
panic(err)
}
r, err := serviceClient.UploadFile(ctx, "test-container", "sample.txt", f, &azblob.UploadFileOptions{})
if err != nil {
panic(err)
}
fmt.Printf("%v\n", r2)
とはいえ、上書きされたくないときもあると思いますが、他のSDKのように overwrite=false
的な物はなくて、ETAGで制御しないとならないようです。If-None-Match
に *
を指定すると、リソースが存在しない限り実行される振る舞いをするので、それを利用します。
o := &azblob.UploadFileOptions{
AccessConditions: &blob.AccessConditions{
ModifiedAccessConditions: &blob.ModifiedAccessConditions{IfNoneMatch: to.Ptr(azcore.ETagAny)},
},
}
r, err := serviceClient.UploadFile(ctx, "test-container", "sample.txt", f, o)
if err != nil {
panic(err)
}
fmt.Printf("%v\n", r2)
その値の細かい話は以下に書かれています。
BLOB サービス操作の条件付きヘッダーの指定 (REST API) - Azure Storage | Microsoft Learn
ダウンロード
ダウンロードにもいくつか関数があり、File/Stream/Bufferなどがあります。以下は、Streamの例です。レスポンスボディを文字に変換して出力しています。
dr, err := serviceClient.DownloadStream(ctx, "test-container", "sample.txt", nil)
if err != nil {
panic(err)
}
data, err := io.ReadAll(dr.Body)
if err != nil {
panic(err)
}
fmt.Println(string(data))
まとめ
さくっと接続、アップロード、ダウンロードを試してみましたが、これがGoらしいのかはちょっとよく分かりませんが、多分そうなのでしょう。あと、他のSDKに慣れているとちょっととっつきにくいかもしれません。
一応ガイドライン的な物もあるので、紹介しておきます(以前ドラフトですけど)
Go Azure SDK Design Guidelines | Azure SDKs
以上(つづく)