S3のEtagの値はMD5と同じ?

2016–10–24

MD5でファイルが同じかどうかを判定することがあります。 S3ではETAGにMD5値が格納されているという事になっていますが、 結論から言うと、Multipart Updateされた場合と、そうでない場合で異なります。

s3 cpコマンドで試してみる(9M)

9Mのテストファイルを作ります。

$ dd if=/dev/zero of=~/test9M bs=1M count=9
9+0 レコード入力
9+0 レコード出力
9437184 バイト (9.4 MB) コピーされました、 0.0269213 秒、 351 MB/秒	  

Bucketにコピーします。

$ aws s3 cp ~/test9M  s3://gside-test/test9M
upload: ../test9M to s3://gside-test/test9M	  

このコマンドでアップロードされた9Mのファイルは、ETAGがこんな感じ

 $ aws s3api head-object --bucket gside-test --key test9M
{
    "Metadata": {},
    "ETag": "\"d126ef08817d0490e207e456cb0ae080-2\"",
    "ContentLength": 9437184,
    "LastModified": "Sat, 22 Oct 2016 14:20:04 GMT",
    "ContentType": "binary/octet-stream",
    "AcceptRanges": "bytes"
}	  

ETagの値は "d126ef08817d0490e207e456cb0ae080-2" とハイフン付きの値です。 一方MD5はというと

$ md5sum ~/test9M
b82b4ab87e44976024abc14a1670dac	  

ETAGの値とは違ってますね。Multipartアップロードされたのが原因です。

s3 cpコマンドで試してみる(8M)

8Mのテストファイルで同様のことをやってみます。

$ dd if=/dev/zero of=~/test8M bs=1M count=8
8+0 レコード入力
8+0 レコード出力
8388608 バイト (8.4 MB) コピーされました、 0.0204059 秒、 411 MB/秒

$ aws s3 cp ~/test8M  s3://gside-test/test8M
upload: ../test8M to s3://gside-test/test8M

$ aws s3api head-object --bucket gside-test --key test8M
{
    "LastModified": "Sat, 22 Oct 2016 14:19:07 GMT",
    "AcceptRanges": "bytes",
    "Metadata": {},
    "ContentType": "binary/octet-stream",
    "ETag": "\"96995b58d4cbf6aaa9041b4f00c7f6ae\"",
    "ContentLength": 8388608
}

$ md5sum ~/test8M
96995b58d4cbf6aaa9041b4f00c7f6ae	  

今度はEtagとMD5が一致しています。Multipartでアップロードされなかったからですね。

解決策

s3api put-objectコマンドを使います。

$ aws s3api put-object --bucket gside-test --key test9M --body ~/test9M
{
    "ETag": "\"b82b4ab87e44976024abc14a1670dac0\""
}

$ aws s3api head-object --bucket gside-test --key test9M
{
    "AcceptRanges": "bytes",
    "ContentLength": 9437184,
    "Metadata": {},
    "ContentType": "binary/octet-stream",
    "ETag": "\"b82b4ab87e44976024abc14a1670dac0\"",
    "LastModified": "Sat, 22 Oct 2016 14:36:02 GMT"
}	  

今度はEtagとMD5が一致しています。

最後に

MD5とETAGを比較して、同一性を検証しているようなスクリプトを書いててハマりました。 AWSコンソールからアップロードした場合は64Mを超える辺りから、Multipart Uploadになるようです。 気をつけましょう。