JavaでファイルのMD5チェックサムを求めて、その結果を16進数表記の文字列として取得したい。

掲題の通り「JavaでファイルのMD5チェックサムを求めて、その結果を16進数表記の文字列として取得したい」という機会があり、四苦八苦して実装したので、そのメモになります。基本的には何かしらの外部ライブラリを利用することをお勧めします(´・ω・｀)

まず、md5チェックサムを求める対象のファイルを以下のようなワンライナーを使って生成します。

```bash
seq 5 | while read x; do cat /dev/urandom | tr -cd 'a-zA-Z0-9' | fold -s5 | xargs -n5 | head -n5 > "sample${x}.txt"; done 
```

これは英数字をランダムに並べたファイルを5つ作成するもので、その内容は以下のようになりました。

```bash
$ find sample*.txt | while read path; do echo "===> ${path} <==="; cat ${path}; done
===> sample1.txt <===
U959b Xxs05 jZ0XH 7Wrew MRmZB
cJlj3 dCEZm qnjbH yy9QM ntuEy
VXKle Se9uc i5UxP mppHQ LnOL2
SNAXl oPGWk JYK7S ovkov JXnJ6
GBlSy frjGp 1wSZ4 Zoiqx ynuaM
===> sample2.txt <===
aSl1p ZNcQt EecBl ie3ox wihqz
svMsD 88Lar BzxXw IfdQT 0QNHI
W3SD4 NQM76 5bZh5 BFhQs G7lmt
LkNq4 cCfMR yOMqX c4AxQ LL7KG
OuOPK GMbH4 IjiGf OFfUK McT3b
===> sample3.txt <===
Eqmhh ioKSo AEMb8 Kdgo8 deFxc
ASilF deSpC 0TIzI ZGM17 OA932
hSGXL LCy8q DDYAC 9hTiF nsDqH
LQRZb k53VV 03NCY RhKjF tVggB
JHmOx wilgw QZov5 StZ2g 9PGjn
===> sample4.txt <===
0Dx9Z 0Md5q w2cUT hp8gq rHIYB
WD6X5 yjyiX DNNyY ZN9Yu IEEkc
P6LnO Zlxwq wSopY Ke4KH oBAWh
3REUX sMPCU 50VYL X14A4 UWuMy
6bttJ 7DuHi i1zxV WlbAS LFANI
===> sample5.txt <===
8wOUf 80uxv zAHmA k9Yml knvKl
o61Cs CREZm APeOv 0wL1w mNIVs
gWELy HtwT4 MWdgT 8VRqs drYhR
6UJvA koZSX qNL9w elNTS CKKne
r2Jop WlMWG 1Qwep 5yvBS 2dtCx
```


そして生成した5つのファイルについて、`md5sum`コマンドを利用し、MD5チェックサムを求めると、以下のようになるはずです。

```
$ md5sum sample*.txt
6777566a705dd85f1f0c2968bfcab2c8  sample1.txt
be520478d070207d07a3335d0ee2e2a4  sample2.txt
4c47ca20b16046a13dbe50869bba368e  sample3.txt
48ca253e12841800cf898ab090a04a0e  sample4.txt
b378511f4b8878c338d602e5376f8426  sample5.txt
```

前置きが長くなりましたが、本題のmd5チェックサムを求めるJavaコードは以下の通りです。

```java
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.DigestInputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.List;


public class Main {
	public static void main(String[] args) throws NoSuchAlgorithmException, IOException {
		var filenames = List.of(
			"sample1.txt",
			"sample2.txt",
			"sample3.txt",
			"sample4.txt",
			"sample5.txt"
		);
		
		for (var filename : filenames) {
			var md5 = md5sum(Paths.get(filename));
			System.out.printf("%s  %s%n", md5, filename);
		}
	}
	
	public static String md5sum(Path path) throws NoSuchAlgorithmException, IOException {
		var digest = MessageDigest.getInstance("MD5");
		try (var input = new DigestInputStream(Files.newInputStream(path), digest)) {
			while (input.read() != -1) {
				// PASS;
			}
			
			var hash = new StringBuilder();
			for (byte b : digest.digest()) {
				hash.append(String.format("%02x", b));
			}
			return hash.toString();
		} 
	}
}
```

これを実行すると、以下のような出力が得られます。`md5sum`コマンドの結果と一致しているので、正しく求めることができたといえそうです(´・ω・｀)

```bash
6777566a705dd85f1f0c2968bfcab2c8  sample1.txt
be520478d070207d07a3335d0ee2e2a4  sample2.txt
4c47ca20b16046a13dbe50869bba368e  sample3.txt
48ca253e12841800cf898ab090a04a0e  sample4.txt
b378511f4b8878c338d602e5376f8426  sample5.txt
```

また未確認ですが、上記Javaコードの`var digest = MessageDigest.getInstance("MD5");`という部分を`var digest = MessageDigest.getInstance("SHA-1");`や`var digest = MessageDigest.getInstance("SHA-256");`に変更すれば、SHA-1やSHA-2チェックサムも求めることができると思います。