判断文件的唯一性--MD5


JAVA中获取文件MD5值的四种方法

 

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


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM