Edited at

S3のChecksum比較でちょっとハマったのでメモ。

いつもの如く、ブログからの転載です。

https://munchkins-diary.hatenablog.com/entry/2019/10/28/230924

最近あんまり書いてないですが、そろそろ再開したい。

S3上のファイルのチェックサムを取得する方法でハマりやすいとこと、下の方におまけでJavaでfileのチェックサムを計算する方法を書いておきました。

誰かの役に立てば幸いです。

(なおあまり)


S3のチェックサムを比較したい

数GBのファイルをS3に挙げるような処理を書いていて、後続処理の失敗などでリトライしたいというのがあった。

この場合、ファイルをアップロードし直すのはムダだが、移行元ストレージ内の対象ファイルが変更にされている可能性もある。

したがって、すでにS3上に完全に同じファイルがあるときだけアップロードをリトライしたい。

この場合、チェックサムを比較するのが楽なので、S3のgetObjectMetaData APIを利用して以下のようにコードを書いた。

 private boolean shouldSkip(String bucketName, String key, String md5CheckSum) {

try {
ObjectMetadata meta = s3Client.getObjectMetadata(bucketName, key);
if (meta == null || meta.getContentMD5() == null) {
log.info("meta data not exist for the file {} in bucket {}", key, bucketName);
return false;
}
log.info(
"Checksum of existing file is {} and present file checksum is {}",
meta.getContentMD5(),
md5CheckSum);
return meta.getContentMD5().equals(md5CheckSum);
} catch (SdkClientException e) {
log.error("Exception thrown while validating the checksum of the file {}", key, e);
return false;
}
}

しかし、どうもうまく行かない。

ObjectMetaData#contentMD5がどうしてもNullになってしまうのだ。

調べたところ、S3における既存オブジェクトのチェックサムはcontentMD5ではなく、Etagに付与されるらしい。

じゃあcontentMD5は何に使うかと言うと、更新時にHTTPヘッダに付与されて、S3内での改ざん確認(正しい使い方)に使うため、getでオブジェクトを取得する時は返されないのだとか。

したがって、S3から落として来たファイルのチェックサムを知りたい時はEtagと比較する必要がある。

こんな感じ。

    private boolean shouldSkip(String bucketName, String key, String md5CheckSum) {

try {
ObjectMetadata meta = this.s3Client.getObjectMetadata(bucketName, key);
if (meta == null || meta.getETag() == null) {
log.info("meta data not exist for the file {} in bucket {}", key, bucketName);
return false;
}
log.info(
"Checksum of existing file is {} and present file checksum is {}",
meta.getETag(),
md5CheckSum);
return meta.getETag().equals(md5CheckSum);
} catch (SdkClientException e) {
log.error("Exception thrown while validating the checksum of the file {}", key, e);
return false;
}
}

これならちゃんと動く。 誰かの役に立てば幸いです。


おまけ Javaでのチェックサム確認方法

チェックサムでググって飛んで来た人のために一応書いておくと、Javaでチェックサムを計算する方法はこんな感じ。

  public static String checkMd5Checksum(File file) {

try (BufferedInputStream is = new BufferedInputStream(new FileInputStream(file))) {
return DigestUtils.md5Hex(is);
} catch (Exception e) {
// Not likely to occur.
log.error(
"ERROR Happened while calculating the check sum for file {}", file.getAbsolutePath(), e);
return "NOT FOUND";
}
}

sha256なら DigestUtils#md5HexDigestUtils#sha256Hexに変えるだけ。

以上、メモでした。