(一)問題
項(xiàng)目中需要對(duì)文件做md5sum,分兩步走:1、對(duì)文件流的每個(gè)字節(jié)用md5實(shí)例進(jìn)行update,然后進(jìn)行digest。2、digest返回長(zhǎng)度為16的byte數(shù)組,一般我們需要把byte數(shù)組轉(zhuǎn)成16進(jìn)制字符串(很多開源的md5加密算法如此實(shí)現(xiàn),真正的原因還不是很理解,可能是便于查看和傳輸)。具體的實(shí)現(xiàn)代碼如下:
/**
* 對(duì)文件進(jìn)行md5 sum操作
* @param checkFile 要進(jìn)行做md5 sum的文件
* @return
*/
public static String md5sum(File checkFile){
String md5sumResult = "";
if(checkFile == null || (!checkFile.exists())){
return md5sumResult;
}
MessageDigest digest = MessageDigest.getInstance("MD5");
InputStream is = new FileInputStream(checkFile);
byte[] buffer = new byte[8192];
int read = 0;
try {
while( (read = is.read(buffer)) > 0) {
digest.update(buffer, 0, read);
}
byte[] md5sum = digest.digest();
BigInteger bigInt = new BigInteger(1, md5sum);
md5sumResult = bigInt.toString(16);
}
catch(IOException e) {
throw new RuntimeException("Unable to process file for MD5", e);
}
finally {
try {
is.close();
}
catch(IOException e) {
throw new RuntimeException("Unable to close input stream for MD5 calculation", e);
}
}
return md5sumResult;
}
其中黃色背景色的轉(zhuǎn)換方式是有問題的。為什么用bigint轉(zhuǎn)16進(jìn)制會(huì)有問題呢,原因是bigint進(jìn)行16進(jìn)制轉(zhuǎn)換的時(shí)候第一個(gè)0被自動(dòng)去掉了.
(二)正確解決方式
那正確的方式是怎么樣的呢?下面有兩種不同的轉(zhuǎn)換方式,但是原理其實(shí)是一致的。
第一種正確的方式(由王建提供):
/**
* 將字節(jié)數(shù)組轉(zhuǎn)換為16進(jìn)制字符串
*
* @param buffer
* @return
*/
public static String toHex(byte[] buffer) {
StringBuffer sb = new StringBuffer(buffer.length * 2);
for (int i = 0; i < buffer.length; i++) {
sb.append(Character.forDigit((buffer[i] & 240) >> 4, 16));
sb.append(Character.forDigit(buffer[i] & 15, 16));
}
return sb.toString();
}
第二種正確的方式:
public static String bytes2HexString(byte[] b) {
String ret = "";
for (int i = 0; i < b.length; i++) {
String hex = Integer.toHexString(b[i] & 0xFF);
if (hex.length() == 1) {
hex = '0' + hex;
}
ret += hex;
}
return ret;
}
(三)問題分析
Md5算法對(duì)任何長(zhǎng)度的字符串進(jìn)行編碼最后輸出是128位長(zhǎng)整數(shù),也就是長(zhǎng)度為16的byte數(shù)組。我們項(xiàng)目調(diào)用的是jdk實(shí)現(xiàn)的md5算法,所以一般是沒問題的。
接下來我們要處理的事情,分別循環(huán)數(shù)組,把每個(gè)字節(jié)轉(zhuǎn)換成2個(gè)16進(jìn)制字符,也就是說每4位轉(zhuǎn)成一個(gè)16進(jìn)制字符。
上面正確的兩種方式也就是做了這樣的事情。
第一種方式:
Character.forDigit((buffer[i] & 240) >> 4, 16)把字節(jié)的高4位取出右移4位換算成int,然后通過forDigit轉(zhuǎn)換成16進(jìn)制字符
Character.forDigit(buffer[i] & 15, 16)把字節(jié)的低4位取出換算成int,然后通過forDigit轉(zhuǎn)換成16進(jìn)制字符
第二種方式:
Integer.toHexString(b[i] & 0xFF)把整個(gè)字節(jié)轉(zhuǎn)成int,然后toHexString也就是做高4位和低4位的運(yùn)算。但是這個(gè)方法如果高四位是0的話就不輸出任何東西,
所以在輸出的字符前加0即可。
b[i] & 0xFF就是把byte轉(zhuǎn)成int,為什么用與oxff做與運(yùn)算,是因?yàn)槿绻?/span>b[i]是負(fù)數(shù)的話,從8位變成32位會(huì)補(bǔ)1,所以需要與0xff做與運(yùn)算,可以把前面的24位全部清零,又可以表示成原來的字節(jié)了。
附:
盡量使用開源提供的工具包,比如:
org.apache.commons.codec.digest.DigestUtils.md5Hex(InputStream data)來對(duì)文件流進(jìn)行md5即可,更加方便,可靠。