Azure Blob Storageを操作する ~レボリューションズ~

別記事の続きです。
Azure Storageは従量課金なので、できるだけ通信量や消費容量を削減したいですね。Azure側でのレプリケーション設定(冗長設定)も地味に差が出ますが、アップロード・ダウンロードに気を付けたいところ。

今回は通信量&消費容量を削減するため、Zipで圧縮して転送することにします。
そもそも何等かの形式で圧縮されている動画や画像にはあまり効果はありませんが、ソースコードの類には多少効くはず。一時ファイルを経由するのが面倒なので、オンザフライに読み書きするように変更していますが、それ以外は前回までと同じ実装です。

引き続き検証環境はPython3.6+Windows 10 Pro、実装はPython3前提です。

もくじ:

スクリプト

今回のスクリプトを記載しておきます。メソッドの構成は前回と変わっていません。

ファイルを圧縮して転送する

一時的にZIP書庫ファイルを作成するやり方もありますが、制御が面倒くさくなるのでio.BytesIO()を使ってメモリ上に確保してしまいます。
PythonでWebアプリケーションを作る場合にけっこう使われるやり方なので、他のREST APIを使うときも役立つかも知れません。

なお、ioモジュールはPython3世代で仕様変更が入っているため、今回のコードはPython2では動きません。たしかPython2ではString.StringIO()を使っていた記憶がありますが今回は触れません。

アップロード

おなじみzipfileモジュールでローカルのファイルを圧縮します。
zipfile.ZipFileはファイルシステム上の実体のあるファイルだけでなく、file-like objectを受け付けるのでここにByteIOストリームを渡します。
# こういう柔軟なところがPythonの良さですね。

圧縮された結果をBlockBlobService.create_blob_from_streamメソッドを使ってアップロードします。

また、圧縮されていることを示すためにメタデータに書くパスにアスタリスク*を付加しています。別途フラグを作っても良いのですが、面倒だったので。

ダウンロード

圧縮付きのBLOBをダウンロードする際には、メモリ上へダウンロードした後に解凍をかけます。

圧縮付きであるか否かの判定はメタデータのパス(アスタリスクが含まれるか否か)で判別します。

途中のディレクトリはZIP書庫解凍時に暗黙的に作成されるので、os.makedirs()を省略しています。

差分のみのファイル転送

ここでは差分を判定して、不要な転送を防ぐ例を記載しておきます。差分といってもファイル単位になりますが、BLOBのメタデータには更新日時などの属性を記録しているので、これらを比較して異なるもののみを対象にアップロード・ダウンロードします。

アップロード

ローカルにあるファイル、所定コンテナ内のBLOBは、それぞれ_blob_client.fetch_localメソッドと_blob_client.fetch_remoteメソッドで取得できます。

それぞれ構成要素はリストに格納されているので、リスト要素を比較することで、BLOBに記録された更新日時よりも新しいローカルファイル、またはまだアップロードされていないローカルファイルをアップロードの対象とします。
ついでに、より新しいファイルのアップロードにより不要になったBLOBは削除します。

この実装では当たり前ですが、同じファイルの名前を変えた場合には別のファイルとしてカウントされるので対応できません。ちゃんとやろうとするとハッシュを計算するのがより正確な気がしますが、まあ今回は脱線になるので割愛。

実行結果は下記のようになります。ちょっと長いですが、試してみるとローカルで更新されていてアップロードされていないものが対象になっていることがわかると思います。

ダウンロード

内容を比較することろはアップロードの場合と基本的には同じです。パスが重複した場合は、更新日時の新しいBLOBがあった場合のみダウンロードの対象とします。

ローカルファイルの場合、パスが同じならば後にダウンロードしたものにより重複したファイルが上書きされるので、明示的に削除する必要はありません。

実行結果の例は下記のようになります。ローカルに存在せず、BLOBとしてアップロードされているもののみをダウンロードしています。

実際に試してみると、心持ち圧縮されている気がしますね。
# BLOB名は違いますが同じファイルの圧縮・非圧縮バージョン↓