2012年10月9日 星期二

在 Java 中計算檔案的 MD5/SHA1 hash

MD5 hash 常用在判斷兩個檔案的內容是否完全相同
一般用來判斷某個檔案是否上傳或下載完整~當然也可以用在檔案複製貼上等等的動作。
之前在做這種需求時,都是直接用 Runtime 呼叫 Linux 的 md5sum
不過總是覺得一直依賴底層 OS 的指令,好像不是個非常完美的作法!
尤其考慮到程式未來可能需要移植到別的平台時,大量使用 Runtime 會導致移植時要花費的功夫暴增。

今天稍微查了一下,原來 Java 原生就有可以計算 MD5/SHA1 hash 的方法!
雖然不清楚 Java 算出來的 hash 值是否跟用其他方式算出來的 hash 值相同,但老實說那並不是很重要 XD
畢竟 MD5 hash 這種東西只要能確認同一個檔案,每次算出來都一樣;不同檔案算出來都不一樣,能達到這個目的就好了!

以下是用 MD5 查詢 hash 值的程式碼,程式碼基本上是參考 [2] 的範例的。
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.security.DigestInputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Formatter;

public class MD5hash {
  public static String getMD5Hash (File file) throws IOException {
    if(!file.exists())
      throw new IOException("The file is not exist."); 
    
    FileInputStream fis = null;
    DigestInputStream dis = null;
    byte[] buff = new byte[1024];
    try {
      MessageDigest md = MessageDigest.getInstance("MD5");
      fis = new FileInputStream(file);
      dis = new DigestInputStream(fis, md);
      
      // Read bytes from the file.
      while(dis.read(buff) != -1);
      
      byte[] md5Digests = md.digest();
      return byteArray2Hex(md5Digests);
    } catch (IOException e) {
      e.printStackTrace();
    } catch (NoSuchAlgorithmException e) {
      e.printStackTrace();
    } finally {
      buff = null;
      if(fis != null) fis.close();
      if(dis != null) dis.close();
    }
    return null;
  }
  
  /**
   * Convert byte array into hex.
   * @param hash
   * @return
   */
  public static String byteArray2Hex(byte[] hash) {
        Formatter formatter = new Formatter();
        for (byte b : hash) {
            formatter.format("%02x", b);
        }
        return formatter.toString();
    }
}

其中 18 行是指定 hash 演算法的地方,如果想要用 SHA1 演算法的話,把 "MD5" 改成 "SHA1" 即可。

2012-10-15 補充
如果在複製檔案時,複製完再重新掃一次檔案算 MD5 hash,感覺是有點浪費時間
因此稍微查了一下,實際上在做 FileInputStream、FileOutputStream 時,是可以直接邊讓他算 hash 的
詳情可以參考 [3] 的範例。
但剛剛測試下來,好像是沒什麼差 XD,也許是我測試的檔案容量(159KB)太小了吧!

參考資料:
1、Getting a File's MD5 Checksum in Java
2、SHA1 and MD5 checksums in Java
3、DigestInputStream --还是与下载有关的

沒有留言: