JAVA中獲取文件MD5值的四種方法其實都很類似,因為核心都是通過JAVA自帶的MessageDigest類來實現。獲取文件MD5值主要分為三個步驟,第一步獲取文件的byte信息,第二步通過MessageDigest類進行MD5加密,第三步轉換成16進制的MD5碼值。幾種方法的不同點主要在第一步和第三步上。具體可以看下面的例子:
方法一、
1 private final static String[] strHex = { "0", "1", "2", "3", "4", "5", 2 "6", "7", "8", "9", "a", "b", "c", "d", "e", "f" }; 3 4 public static String getMD5One(String path) { 5 StringBuffer sb = new StringBuffer(); 6 try { 7 MessageDigest md = MessageDigest.getInstance("MD5"); 8 byte[] b = md.digest(FileUtils.readFileToByteArray(new File(path))); 9 for (int i = 0; i < b.length; i++) { 10 int d = b[i]; 11 if (d < 0) { 12 d += 256; 13 } 14 int d1 = d / 16; 15 int d2 = d % 16; 16 sb.append(strHex[d1] + strHex[d2]); 17 } 18 } catch (NoSuchAlgorithmException e) { 19 e.printStackTrace(); 20 } catch (IOException e) { 21 e.printStackTrace(); 22 } 23 return sb.toString(); 24 }
方法一是比較原始的一種實現方法,首先將文件一次性讀入內存,然后通過MessageDigest進行MD5加密,最后再手動將其轉換為16進制的MD5值。
方法二、
1 public static String getMD5Two(String path) { 2 StringBuffer sb = new StringBuffer(""); 3 try { 4 MessageDigest md = MessageDigest.getInstance("MD5"); 5 md.update(FileUtils.readFileToByteArray(new File(path))); 6 byte b[] = md.digest(); 7 int d; 8 for (int i = 0; i < b.length; i++) { 9 d = b[i]; 10 if (d < 0) { 11 d = b[i] & 0xff; 12 // 與上一行效果等同 13 // i += 256; 14 } 15 if (d < 16) 16 sb.append("0"); 17 sb.append(Integer.toHexString(d)); 18 } 19 } catch (NoSuchAlgorithmException e) { 20 e.printStackTrace(); 21 } catch (IOException e) { 22 e.printStackTrace(); 23 } 24 return sb.toString(); 25 }
方法二與方法一不同的地方主要是在步驟三,這里借助了Integer類的方法實現16進制的轉換,比方法一更簡潔一些。PS:JAVA中byte是有負數的,代碼中&0xff的操作與計算機中數據存儲的原理有關,即負數存儲的是二進制的補碼,有興趣的童鞋可以挖一下,這里不展開說。
方法三、
1 public static String getMD5Three(String path) { 2 BigInteger bi = null; 3 try { 4 byte[] buffer = new byte[8192]; 5 int len = 0; 6 MessageDigest md = MessageDigest.getInstance("MD5"); 7 File f = new File(path); 8 FileInputStream fis = new FileInputStream(f); 9 while ((len = fis.read(buffer)) != -1) { 10 md.update(buffer, 0, len); 11 } 12 fis.close(); 13 byte[] b = md.digest(); 14 bi = new BigInteger(1, b); 15 } catch (NoSuchAlgorithmException e) { 16 e.printStackTrace(); 17 } catch (IOException e) { 18 e.printStackTrace(); 19 } 20 return bi.toString(16); 21 }
方法三與前面兩個方法相比,在讀入文件信息上有點不同。這里是分多次將一個文件讀入,對於大型文件而言,比較推薦這種方式,占用內存比較少。步驟三則是通過BigInteger類提供的方法進行16進制的轉換,與方法二類似。
方法四、
1 DigestUtils.md5Hex(new FileInputStream(path));
方法四應該是最便捷的吧,哈哈,好東西要留在最后,如果你只需要使用標准的MD5,其實一行代碼就夠了,JAVA自帶的commons-codec包就提供了獲取16進制MD5值的方法。其底層實現上,也是分多次將一個文件讀入,類似方法三。所以性能上也不錯。
總結:其實方法都是類似的,推薦使用方法四,簡潔且性能不錯,當然,如果要做一些調整什么的,可以根據自己的需求進行方法的選擇。
PS:其實還有一個重點,就是如何知道自己生成的MD5值是否正確呢?
方法很多,其實有一個挺簡單的方法,不需要另外安裝什么軟件。使用windows自帶的命令即可:certutil -hashfile [文件路徑] MD5,例子如下:
最后給MD5的Java實現:
public
class
MD5{
/*
*四個鏈接變量
*/
private
final
int
A=
0x67452301
;
private
final
int
B=
0xefcdab89
;
private
final
int
C=
0x98badcfe
;
private
final
int
D=
0x10325476
;
/*
*ABCD的臨時變量
*/
private
int
Atemp,Btemp,Ctemp,Dtemp;
/*
*常量ti
*公式:floor(abs(sin(i+1))×(2pow32)
*/
private
final
int
K[]={
0xd76aa478
,
0xe8c7b756
,
0x242070db
,
0xc1bdceee
,
0xf57c0faf
,
0x4787c62a
,
0xa8304613
,
0xfd469501
,
0x698098d8
,
0x8b44f7af
,
0xffff5bb1
,
0x895cd7be
,
0x6b901122
,
0xfd987193
,
0xa679438e
,
0x49b40821
,
0xf61e2562
,
0xc040b340
,
0x265e5a51
,
0xe9b6c7aa
,
0xd62f105d
,
0x02441453
,
0xd8a1e681
,
0xe7d3fbc8
,
0x21e1cde6
,
0xc33707d6
,
0xf4d50d87
,
0x455a14ed
,
0xa9e3e905
,
0xfcefa3f8
,
0x676f02d9
,
0x8d2a4c8a
,
0xfffa3942
,
0x8771f681
,
0x6d9d6122
,
0xfde5380c
,
0xa4beea44
,
0x4bdecfa9
,
0xf6bb4b60
,
0xbebfbc70
,
0x289b7ec6
,
0xeaa127fa
,
0xd4ef3085
,
0x04881d05
,
0xd9d4d039
,
0xe6db99e5
,
0x1fa27cf8
,
0xc4ac5665
,
0xf4292244
,
0x432aff97
,
0xab9423a7
,
0xfc93a039
,
0x655b59c3
,
0x8f0ccc92
,
0xffeff47d
,
0x85845dd1
,
0x6fa87e4f
,
0xfe2ce6e0
,
0xa3014314
,
0x4e0811a1
,
0xf7537e82
,
0xbd3af235
,
0x2ad7d2bb
,
0xeb86d391
};
/*
*向左位移數,計算方法未知
*/
private
final
int
s[]={
7
,
12
,
17
,
22
,
7
,
12
,
17
,
22
,
7
,
12
,
17
,
22
,
7
,
12
,
17
,
22
,
5
,
9
,
14
,
20
,
5
,
9
,
14
,
20
,
5
,
9
,
14
,
20
,
5
,
9
,
14
,
20
,
4
,
11
,
16
,
23
,
4
,
11
,
16
,
23
,
4
,
11
,
16
,
23
,
4
,
11
,
16
,
23
,
6
,
10
,
15
,
21
,
6
,
10
,
15
,
21
,
6
,
10
,
15
,
21
,
6
,
10
,
15
,
21
};
/*
*初始化函數
*/
private
void
init(){
Atemp=A;
Btemp=B;
Ctemp=C;
Dtemp=D;
}
/*
*移動一定位數
*/
private
int
shift(
int
a,
int
s){
return
(a<<s)|(a>>>(
32
-s));
//右移的時候,高位一定要補零,而不是補充符號位
}
/*
*主循環
*/
private
void
MainLoop(
int
M[]){
int
F,g;
int
a=Atemp;
int
b=Btemp;
int
c=Ctemp;
int
d=Dtemp;
for
(
int
i =
0
; i <
64
; i ++){
if
(i<
16
){
F=(b&c)|((~b)&d);
g=i;
}
else
if
(i<
32
){
F=(d&b)|((~d)&c);
g=(
5
*i+
1
)%
16
;
}
else
if
(i<
48
){
F=b^c^d;
g=(
3
*i+
5
)%
16
;
}
else
{
F=c^(b|(~d));
g=(
7
*i)%
16
;
}
int
tmp=d;
d=c;
c=b;
b=b+shift(a+F+K[i]+M[g],s[i]);
a=tmp;
}
Atemp=a+Atemp;
Btemp=b+Btemp;
Ctemp=c+Ctemp;
Dtemp=d+Dtemp;
}
/*
*填充函數
*處理后應滿足bits≡448(mod512),字節就是bytes≡56(mode64)
*填充方式為先加一個0,其它位補零
*最后加上64位的原來長度
*/
private
int
[] add(String str){
int
num=((str.length()+
8
)/
64
)+
1
;
//以512位,64個字節為一組
int
strByte[]=
new
int
[num*
16
];
//64/4=16,所以有16個整數
for
(
int
i=
0
;i<num*
16
;i++){
//全部初始化0
strByte[i]=
0
;
}
int
i;
for
(i=
0
;i<str.length();i++){
strByte[i>>
2
]|=str.charAt(i)<<((i%
4
)*
8
);
//一個整數存儲四個字節,小端序
}
strByte[i>>
2
]|=
0x80
<<((i%
4
)*
8
);
//尾部添加1
/*
*添加原長度,長度指位的長度,所以要乘8,然后是小端序,所以放在倒數第二個,這里長度只用了32位
*/
strByte[num*
16
-
2
]=str.length()*
8
;
return
strByte;
}
/*
*調用函數
*/
public
String getMD5(String source){
init();
int
strByte[]=add(source);
for
(
int
i=
0
;i<strByte.length/
16
;i++){
int
num[]=
new
int
[
16
];
for
(
int
j=
0
;j<
16
;j++){
num[j]=strByte[i*
16
+j];
}
MainLoop(num);
}
return
changeHex(Atemp)+changeHex(Btemp)+changeHex(Ctemp)+changeHex(Dtemp);
}
/*
*整數變成16進制字符串
*/
private
String changeHex(
int
a){
String str=
""
;
for
(
int
i=
0
;i<
4
;i++){
str+=String.format(
"%2s"
, Integer.toHexString(((a>>i*
8
)%(
1
<<
8
))&
0xff
)).replace(
' '
,
'0'
);
}
return
str;
}
/*
*單例
*/
private
static
MD5 instance;
public
static
MD5 getInstance(){
if
(instance==
null
){
instance=
new
MD5();
}
return
instance;
}
private
MD5(){};
public
static
void
main(String[] args){
String str=MD5.getInstance().getMD5(
""
);
System.out.println(str);
}
}