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);
}
}