C#高速MD5檔案雜湊值產生器

今日工作上有一個基於備份的角度,需要一個運算檔案雜湊值得需求,因為有自己的功能性要求,又不放心使用網路上的執行檔,因此乾脆自己寫一個比較妥當。

MD5演算法當然是直接使用.NET Framework提供的System.Security.Cryptography命名空間來進行設計啦,但這裡面有一個盲點,就是如果你沒有針對FileStream調整讀取的Buffer緩衝區空間,那麼在預設為4096 Bytes的基礎下,MD5運算的速度整個塞在頻繁的Disk IO上,算GB等級的檔案少說也要30分鐘起跳,真的是慢爆啦!

高速MD5運算程式碼

其實所謂的高速不過就是把緩衝區調整大一點而已,以下面的程式碼為例,我是用100MB來當作緩存,如果你有自己的需要可以再調整更高一些。另外我也設計了一個運算時的Console狀態顯示指標,以免在運算大型檔案時,會讓人有算到當機的誤解。程式碼如下:

class Program
{
  //運算狀態字元
  public static int iPrcoessState = 0;
  //目前座標
  public static int iCurrY = 0;
  public static int iCurrX = 0;
  //狀態更新計時器
  public static System.Timers.Timer oTimer = new System.Timers.Timer(30);
  //設定雜湊讀取緩衝區
  public static int iBufferBytes = 1024 * 1024 * 100; //100MB

  static void Main(string[] args)
  {
    //目錄下現有的檔案列舉
    System.IO.FileInfo[] oFileList;
    //偵測操作模式
    if (args.Length == 0)
    {
      //使用自動抓取多檔案模式
      try
      {
        System.IO.DirectoryInfo oDI = new System.IO.DirectoryInfo(System.Environment.CurrentDirectory);
        oFileList = oDI.GetFiles();
      }
      catch
      {
        WriteLine("列舉執行路徑下所有檔案時發生錯誤。");
        return;
      }
      //查看目錄下是否有檔案
      if (oFileList.Length == 0) { WriteLine("此執行路徑下沒有任何檔案。"); return; }
    }
    else
    {
      //使用手動設定單一檔案模式
      System.Collections.Generic.List<System.IO.FileInfo> oList = new System.Collections.Generic.List<System.IO.FileInfo>();
      foreach (string cFile in args)
      {
        if (System.IO.File.Exists(cFile)) { oList.Add(new System.IO.FileInfo(cFile)); }
      }
      if (oList.Count > 0)
      {
        oFileList = oList.ToArray();
      }
      else
      {
        WriteLine("系統偵測到使用單一檔案模式,但所列舉的檔案裡沒有任何檔案是合法存在的。");
        return;
      }
    }
    //開始進行雜湊工作
    //掛載事件
    oTimer.Elapsed += PrintProcessState;
    //進入處理
    foreach (System.IO.FileInfo oItem in oFileList)
    {
      Write($" # {oItem.Name}: ");
      iCurrY = CursorTop;
      iCurrX = CursorLeft;
      //啟動計時器
      oTimer.Start();
      string cHash = "";
      using (System.IO.FileStream oFS = new System.IO.FileStream(
        oItem.Name,
        System.IO.FileMode.Open,
        System.IO.FileAccess.Read,
        System.IO.FileShare.ReadWrite,
        iBufferBytes))
      {
        System.Security.Cryptography.MD5 oHash = System.Security.Cryptography.MD5.Create();
        cHash = BitConverter.ToString(oHash.ComputeHash(oFS)).Replace("-", "");
      }
      //終止計時器
      oTimer.Stop();
      //將運算狀態指示器座標洗掉
      CursorTop = iCurrY;
      CursorLeft = iCurrX;
      //印出雜湊
      WriteLine(cHash);
    }
    //關掉資源
    oTimer = null;
  }

  /// <summary>
  /// 純粹顯示目前的執行狀況
  /// </summary>
  /// <param name="sender"></param>
  /// <param name="e"></param>
  public static void PrintProcessState(Object sender, EventArgs e)
  {
    CursorTop = iCurrY;
    CursorLeft = iCurrX;
    string cTemp = "";
    switch (iPrcoessState)
    {
      case 0:
        cTemp = @"-";
        iPrcoessState++;
        break;
      case 1:
        cTemp = @"\";
        iPrcoessState++;
        break;
      case 2:
        cTemp = @"|";
        iPrcoessState++;
        break;
      case 3:
        cTemp = @"/";
        iPrcoessState = 0;
        break;
    }
    Write(cTemp);
  }
}  

經過實際運算測試,拿一個「CentOS-7-x86_64-DVD-1503-01.ISO」大約4.xGB檔案來運算,在4代i7等級的CPU運算時間落在8秒到9秒之間,算是可以接受的速度了。

C:\>MD5Hash.exe CentOS-7-x86_64-DVD-1503-01.iso
 # CentOS-7-x86_64-DVD-1503-01.iso: 99E450FB1B22D2E528757653FCBF5FDC

懶得編譯的人,我這邊也有將編譯完成的檔案(bin、exe)上傳了,歡迎下載使用。支援兩種動作,無參數的模式下,會自動抓取執行檔案現在的目錄下的所有檔案,全部算一次MD5;有參數的模式下,參數即為檔案名稱,他只會運算你指定檔案的雜湊值,例如:「MD5Hash.exe A.zip B.tar」。

Fast MD5 hashcode Download

System.Security.Cryptography Fast Boost MD5 SHA1 Hash