Google Cloud Storage の md5チェックサム を Pythonで計算する

1 min読了の目安(約1400字TECH技術記事

結論

  • gsutilで得られるmd5チェックサムはRFC1864で書かれているContent-MD5ヘッダと同じ規則で算出されたものになる
  • というわけで記事タイトルは「Content-MD5をPythonで計算する」と同義である

背景

課題: GCSにアップロードしたファイル間で間違って重複したファイルを送ってないか調査したい
制約: gsutilコマンドが利用できない(利用できるならgsutil hashで瞬殺できるはず)

md5チェックサムによるファイルの整合性検証

よくあるやり方としてmd5チェックサムを取得し比較するというものがあるので、まずはこれを試してみることにする。

GCSは標準の仕様としてオブジェクト単位でmd5チェックサムを提供してくれるので、これを利用すると簡単そうに見える。

今回、制約の通りgsutilは使えないのでpythonを使う。

from google.cloud import storage
client = storage.Client()
bucket = client.get_bucket("my-test-bucket000")
blob = bucket.blob("test.txt")

blob.reload()
blob.md5_hash

結果"2Oj8otwPiW/Xy0ywAxuiSQ=="という結果が返ってきた。この文字列でムッと思う人も多い気がする。

続いて同じファイルをmd5チェックサムをpythonで計算してみる

import hashlib
with open("test.txt", "rb") as f:
    print(hashlib.md5(f.read()).hexdigest())

結果"d8e8fca2dc0f896fd7cb4cb0031ba249" という値で全く合わない。
(読みにくいのでhexdigestにしていますが、この先使うのはdigestです)

base64 + md5

先程のGCS側で確認した"2Oj8otwPiW/Xy0ywAxuiSQ=="だが、HTTPの流れでいうとBase64でエンコードされた文字列に似ているな、とか思うわけで、となるとContent-MD5ヘッダがなにかありそうだということで調べてみる。

冒頭でも書いたRFC1864だが、記載の通り、128bitのMD5ダイジェストを取得した後にbase64エンコードをかけるようだ。
ということで改めて計算してみる。

import hashlib
import base64
with open("test.txt", "rb") as f:
    print(print(base64.b64encode(hashlib.md5(f.read()).digest())))

結果'2Oj8otwPiW/Xy0ywAxuiSQ=='というGCS上の結果と一致する