判斷文件的唯一性--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