網路上常見將檔案完整讀入記憶體再計算 MD5 的做法,不僅記憶體效率差,且對大檔案可能導致 OOM。本文分析常見錯誤,提供正確的流式計算方法。
File file = new File("d:/1.dat");
FileInputStream fs = new FileInputStream(file);
BufferedInputStream bi = new BufferedInputStream(fs);
ByteArrayOutputStream bo = new ByteArrayOutputStream();
byte[] b = new byte[bi.available()]; // 危險:嘗試一次配置全檔案大小記憶體
int i;
while ((i = bi.read(b, 0, b.length)) != -1) {
bo.write(b, 0, i);
}
bo.close();
System.out.print(Md5.MD5(new String(bo.toByteArray()))); // 錯誤:位元組轉字串再雜湊,結果不正確
bi.available() 回傳剩餘位元組數,大檔案直接 OOMnew String(byte[]) 依平台預設編碼解碼,可能導致資料損壞/不一致,MD5 必須直接處理原始位元組import java.io.*;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
public class FileMD5 {
public static String calculate(File file) throws IOException, NoSuchAlgorithmException {
MessageDigest md = MessageDigest.getInstance("MD5");
try (FileInputStream fis = new FileInputStream(file);
BufferedInputStream bis = new BufferedInputStream(fis)) {
byte[] buffer = new byte[8192]; // 8KB 緩衝區
int bytesRead;
while ((bytesRead = bis.read(buffer)) != -1) {
md.update(buffer, 0, bytesRead); // 邊讀邊更新雜湊
}
}
byte[] digest = md.digest();
return bytesToHex(digest);
}
private static String bytesToHex(byte[] bytes) {
char[] hexDigits = "0123456789abcdef".toCharArray();
char[] result = new char[bytes.length * 2];
for (int i = 0, k = 0; i < bytes.length; i++) {
byte b = bytes[i];
result[k++] = hexDigits[b >>> 4 & 0xF];
result[k++] = hexDigits[b & 0xF];
}
return new String(result);
}
}
若需支援斷點驗證,可結合 RandomAccessFile 分段計算,或採用分塊雜湊樹(如 BT 的 piece hash),但核心原理相同:流式處理原始位元組。
正確的 MD5 計算不僅準確,更能優雅處理任意大小檔案,是檔案完整性驗證的基石。