Java 获取文件 MD5

上周开始学习 Java 语言,作为练手项目,写了一个工具,功能就是从 S3 下载文件,再上传到另一个 S3 上,因为是大文件,为了避免重复下载上传,需要获取文件 md5 与 S3 eTag 进行对比,如果相同,就不重复下载上传。

于是我打开 Google 输入了关键词 Java md5,搜索结果主要代码如

1
File file = new File(FILE_NAME);

1
2
3
4
5
FileInputStream stream = new FileInputStream(file);
MessageDigest digest = MessageDigest.getInstance("MD5");
MappedByteBuffer byteBuffer = stream.getChannel().map(FileChannel.MapMode.READ_ONLY, 0, file.length());
digest.update(byteBuffer);
byte[] hash = digest.digest();

这一段问题不大,但是要和 eTag 对比,需要转换成 32 位的 16 进制数字的字符串表达式,搜索引擎给的结果里主要有两种方式

1
2
3
4
5
6
7
8
9
10
StringBuffer hexString = new StringBuffer();
for (int i = 0; i < hash.length; i++) {
if ((0xff & hash[i]) < 0x10) {
hexString.append("0" + Integer.toHexString((0xFF & hash[i])));
} else {
hexString.append(Integer.toHexString(0xFF & hash[i]));
}
}
String result = hexString.toString();

习惯写 PHP 之后,看到这类不能一个方法调用就完成的代码,有抵触心理,于是我选择了复制下面这一种实现

1
2
BigInteger bi = new BigInteger(1, hash);
String result = bi.toString(16);

问题就出现了,由于转换成 BigInteger 再输出字符串,如果第一个数字是 0 的话,就会被忽略,那么得到的结果就不是 32 位的 16 进制数字的字符串了,用这个结果和 eTag 对比就会出现对不上的情况,作为一个伪强迫症犯者,自然不能用判断长度小于 32 位就补 0 的方式。

于是我找到了 apache 开源库的代码 https://github.com/apache/commons-codec/blob/trunk/src/main/java/org/apache/commons/codec/digest/DigestUtils.java,学习人家是如何正确实现的,最后实现如下

1
2
3
4
5
6
7
8
9
10
11
12
FileInputStream stream = new FileInputStream(file);
int length = 1024;
final byte[] buffer = new byte[length];
int read = stream.read(buffer, 0, length);
MessageDigest digest = MessageDigest.getInstance(MessageDigestAlgorithms.MD5);
while (read > -1) {
digest.update(buffer, 0, read);
read = stream.read(buffer, 0, length);
}
String result = Hex.encodeHexString(digest.digest());