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」。