Blobメタデータ登録時の SetMetadata メソッドの呼び出し
こちらのエントリーを書いていて Blob データにメタデータ設定できると知って衝撃を受けた自分のツイートからはじましました。
以前 Blob を利用したアプリを作ったときは(いろいろあってすべて削除しましたが)画像を Blob Storage に保存していましたが、メタデータは別にTable Storage上に保存していたんですよね。ここやここで必死にストレージへのアクセスを高速化していましたが、そもそも必要ないストレージへのアクセスを行なっていたんですね orz … CloudBlob クラスの Metadata プロパティを利用すると、 Blob データとメタデータをまとめて管理することができます。
この自分のつぶやきに対して、「メタデータの登録には SetMetadata() の呼び出しが必要ですよ」との情報を頂きましたが、MSDNのチュートリアルのコード(リンクの2.)を見ると SetMetadata() の呼び出しは行なっていません。すると、「今は SetMetadata() は必要ないの?」という流れになってきました。MSDN の CloudPageBlob.SetMetadata (CloudBlobクラスはCloudPageBlobクラスを継承しています) を見ると、SetMetadataを呼び出さないとメタデータ値は保存されないことが記載されています。
CloudPageBlob.SetMetadata メソッド
http://msdn.microsoft.com/ja-jp/library/ee772815.aspx
【解説】
SetMetadata メソッドは、BLOB の Metadata プロパティで指定されているメタデータ値を、サービスに書き込みます。 Metadata プロパティを設定しても、BLOB 参照のメタデータ値が設定されるだけです。メタデータ値をサービスに書き込むには、BeginSetMetadata または SetMetadata を呼び出す必要があります。
やべぇ、テンション上がってきた!!
MSDNのチュートリアルのコードにバグがあるのか、あまり知られていない事実を見つけたのかどちらかです。さっそくMSDNのチュートリアルのコードを実行してみます。
private void SaveImage(string id, string name, string description, string tags,
string fileName, string contentType, byte[] data)
{
// blobコンテナを作成し、画像のbyte配列をアップロードする
var blob = this.GetContainer().GetBlobReference(name);
blob.Properties.ContentType = contentType;
// 画像のメタデータを作成する
var metadata = new NameValueCollection();
metadata["id"] = id;
metadata["Filename"] = fileName;
metadata["ImageName"] = string.IsNullOrEmpty(name) ? "unknown" : name;
metadata["Description"] = string.IsNullOrEmpty(description) ? "unknown" : description;
metadata["Tags"] = string.IsNullOrEmpty(tags) ? "unknown" : tags;
// blobへデータを追加し、コミットする
blob.Metadata.Add(metadata);
blob.UploadByteArray(data);
}
登録できました (゚A゚;)ゴクリ Azure Storage Explorer で確認すると、きちんとデータが登録されていることが分かります。
では、コードにSetMetadata()の呼び出しを追加します。StorageClientExceptionが発生しました \(^o^)/
private void SaveImage(string id, string name, string description, string tags,
string fileName, string contentType, byte[] data)
{
// blobコンテナを作成し、画像のbyte配列をアップロードする
var blob = this.GetContainer().GetBlobReference(name);
blob.Properties.ContentType = contentType;
// 画像のメタデータを作成する
var metadata = new NameValueCollection();
metadata["id"] = id;
metadata["Filename"] = fileName;
metadata["ImageName"] = string.IsNullOrEmpty(name) ? "unknown" : name;
metadata["Description"] = string.IsNullOrEmpty(description) ? "unknown" : description;
metadata["Tags"] = string.IsNullOrEmpty(tags) ? "unknown" : tags;
// -- SetMetadataの呼び出しを追加してみる
blob.Metadata.Add(metadata);
blob.SetMetadata();
blob.UploadByteArray(data);
}
( ゜д゜) ポカーン
「Blob のメタデータ登録にはSetMetadata()の呼び出しが必要だよ」
↓
SetMetadata()の呼び出しなしでもちゃんと登録できた(MSDNのチュートリアルは間違ってない)
↓
試しにSetMetadata()を追加してみたら、StorageClientException が発生 ← イマココ
え?何??事前に聞いてた話と全然違うんですけどwww MSDNの「CloudPageBlob.SetMetadata メソッド」のサンプルコードと、「Exploring Windows Azure Storage」のコードを見比べて見る… あ、
Blobデータを先に登録した場合は、メタデータの登録にSetMetadata()の呼び出しが必要かもしれない
論より証拠です、Blobデータを登録して SetMetadata() の呼び出しを行わないコードを書いてみます。
private void SaveImage(string id, string name, string description, string tags,
string fileName, string contentType, byte[] data)
{
// blobコンテナを作成し、画像のbyte配列をアップロードする
var blob = this.GetContainer().GetBlobReference(name);
blob.Properties.ContentType = contentType;
// 画像のメタデータを作成する
var metadata = new NameValueCollection();
metadata["id"] = id;
metadata["Filename"] = fileName;
metadata["ImageName"] = string.IsNullOrEmpty(name) ? "unknown" : name;
metadata["Description"] = string.IsNullOrEmpty(description) ? "unknown" : description;
metadata["Tags"] = string.IsNullOrEmpty(tags) ? "unknown" : tags;
// -- 先にBlobデータを登録してSetMetadataを呼び出さない
blob.UploadByteArray(data);
blob.Metadata.Add(metadata);
}
処理は正常終了しましたが、メタデータが登録されていません。
じゃ、SetMetadata() 呼び出しを追加してみましょう
private void SaveImage(string id, string name, string description, string tags,
string fileName, string contentType, byte[] data)
{
// blobコンテナを作成し、画像のbyte配列をアップロードする
var blob = this.GetContainer().GetBlobReference(name);
blob.Properties.ContentType = contentType;
// 画像のメタデータを作成する
var metadata = new NameValueCollection();
metadata["id"] = id;
metadata["Filename"] = fileName;
metadata["ImageName"] = string.IsNullOrEmpty(name) ? "unknown" : name;
metadata["Description"] = string.IsNullOrEmpty(description) ? "unknown" : description;
metadata["Tags"] = string.IsNullOrEmpty(tags) ? "unknown" : tags;
// -- 先にBlobデータを登録してSetMetadataを呼び出す
blob.UploadByteArray(data);
blob.Metadata.Add(metadata);
blob.SetMetadata();
}
ちゃんとメタデータが登録されました!!
まとめ
- Blobデータよりもメタデータを先に登録 → メタデータの登録にSetMetadataの呼び出しは不要、呼び出すとStorageClientExceptionが発生する
- メタデータよりもBlobデータを先に登録 → メタデータの登録にSetMetadataの呼び出しが必要、呼び出さないとメタデータのみ登録されない
考察(根拠はありません)ですが、Blob Storageは Blob データ(テキスト、画像、動画など)保存時にコミットを行なっているため、Blob データ登録前にメタデータをプロパティにセットしておけば、別途SetMetadataの呼び出しが不要になるのではないかと思います。Blob データよりも先にメタデータを登録することを推奨しておけば、「なぜかメタデータだけ登録されない!!」という悲痛な叫びが減るのではないでしょうか。Azure SDK 1.6で検証してるので、過去バージョンではどのように動作するのか分かりませんが結構重要な(これだけで「Azure使えねぇ」と判断されてしまうのかも)情報だと思います。
今回作成したサンプルコードは以下からダウンロード可能です。「SetMetadata.zip」をダウンロードしてください。
一言
「Blob データを登録する場合は、先にメタデータを登録しておくことを推奨。あとでメタデータを登録する場合はSetMetadata()を必ず呼び出すこと。」
自分のなかでTable Storage と Blob Storage のハマリどころが蓄積されてきたので、年内にまとめてブログにアップしようと思います。