Rubyのaws-sdkを使って、定期的にディレクトリごとs3にアップロードしていて、手元のファイルとs3のファイルが同じかどうかをetagでチェックしていたら、etagがファイルのmd5の場合とそうじゃない場合があって困ったので調べてみた。
Generally the ETAG is the MD5 of the object. If the object was uploaded using multipart upload then this is the MD5 all of the upload-part-md5s.
http://docs.amazonwebservices.com/AWSRubySDK/latest/AWS/S3/S3Object.html#etag-instance_method
ふつうはアップロードしたもののmd5になるけれど、multipart uploadを使ったときは部分ごとのmd5を合わせたもののmd5になる、ということだった。
自分の場合でファイルのmd5とそうじゃないのがあったのは、アップロードするときに使っていたaws-sdkのS3Object#writeが16MBより大きい場合、5MBごとにmultipart uploadをするからだった。この16MBや5MB、それからmultipart uploadをするかどうかはオプションで指定できた。それでmultipart uploadをしなければ一応解決なんだけどmultipart uploadのが速いので、multipart uploadの場合のetagを実際に計算してみた。
分割したデータのmd5(hex表記じゃないそのままのもの)を全部つなげたデータのmd5のhex表記-分割数
こんな感じに計算すればよかった。分割部のmd5はhexじゃないのを使わないといけなくて、ここが分からなくてはまった。Rubyのコードだとこんな感じ。
md5s = []
while !io.eof?
md5s << Digest::MD5.digest(io.read(part_size))
end
r = Digest::MD5.hexdigest(md5s.join('')) + '-' + md5s.size.to_s
gemにしてコマンドラインで使えたり、Rubyのライブラリとして使えるようにしものを作っておいた。
swdyh/s3etag GitHub https://github.com/swdyh/s3etag
インストール
% gem install s3etag
コマンドラインで使う
% s3etag
s3etag file
-t, --threshold threshold
-p, --max-parts max-parts
-s, --min_part_size min_part_size
% s3etag text.txt
91fdac689d4861c9ae7a0afa21a1f6b8-18
% s3etag -s 10000000 text.text
32aafcd9748824e559b4dbd6b908f6fa-10
Rubyのライブラリとして使う
require 's3etag'
p S3Etag.calc(:data => 'a' * 1000)
p S3Etag.calc(:data => 'a' * 1000, :threshold => 100, :min_part_size => 100)
p S3Etag.calc(:file => 'test.txt')
p S3Etag.calc(:file => '.text.text', :threshold => 100, :min_part_size => 100)