65.9K
CodeProject 正在变化。 阅读更多。
Home

用于文件哈希比较(MD5 和 SHA1)的快速程序

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.43/5 (3投票s)

2016年4月1日

CPOL

2分钟阅读

viewsIcon

11410

一个用于计算和测量哈希比较的快速程序。

引言

通常,如果我们需要检测文件系统或文件目录中的更改,我们通常会使用 .NET 提供的文件系统观察器。 然而,在了解了它的副作用后,它似乎只是一个建议的类,并没有真正的益处。 不使用文件系统观察器类的另一个原因是,它通常不关心文件内容,而是关心整个文件系统。 因此,我发现哈希是一种更好的方法。

背景

在本文中,我将尝试回答程序员们经常问的一个关于哈希的问题,即计算目录中文件哈希需要多长时间,如果父文件夹中有子文件夹会怎样。 对于具有几兆字节文件大小的正常应用程序部署文件结构来说,速度是否足够快? 为了回答这些问题,我编写了一个小型实用工具,并在我的文件结构上运行它,该结构包含大约 45 个文件,总大小为几兆字节。 结果足够快。 计算哈希仅需 50-60 毫秒,验证哈希也需要相同的时间。

Using the Code

请查看下面的代码文件。 我尝试使用 MD5 和 SHA1 哈希算法计算哈希值。 两种算法计算文件内容的哈希值所需的时间相同。 请注意,我们在这里哈希实际的文件内容。 如果文件内容发生任何更改,即使是一个新的空格或字符,整个文件的哈希值也会更改。 然而,重要的是要注意,文件属性的任何更改,例如上次文件修改时间等,都不会影响哈希结果。

 public class DeploymentFile
    {
        public string FilePath { get; set; }
        public bool IsFilePathValid { get; set; }
        public string HashedValue { get; set; }
        public bool IsFileModified { get; set; }

        public DeploymentFile(string filePath)
        {
            FilePath = filePath;
            IsFilePathValid = true;
            IsFileModified = false;
            if (File.Exists(filePath))
                HashedValue = ComputeHashSHA(filePath);
            else
                IsFilePathValid = false;
        }

        public bool IsExist(string FilePath)
        {
            return File.Exists(FilePath);
        }

        //public string ComputeHashMD5(string filename)
        //{
        //    using (var md5 = MD5.Create())
        //    {
        //        using (var stream = File.OpenRead(filename))
        //        {
        //            return (Encoding.Default.GetString(md5.ComputeHash(stream)));
        //        }
        //    }
        //}

        public string ComputeHashSHA(string filename)
        {
            using (var sha = SHA1.Create())
            {
                using (var stream = File.OpenRead(filename))
                {
                    return (Encoding.Default.GetString(sha.ComputeHash(stream)));
                }
            }
        }
    }

下面显示的是用于显示所有控件的 Form 代码。 您可能会注意到,我正在使用一个秒表来测量整个哈希计算过程所花费的时间。
重要提示:请注意,如果出现消息框,秒表会测量用户点击和关闭消息框的所有时间。 因此,为了准确测量,可以禁用消息框。

public partial class FileValidator : Form
    {
        public FileValidator()
        {
            InitializeComponent();
        }
        List<DeploymentFile> DeployList;
        List<DeploymentFile> ValidationList;
        String filePath;
        

        #region ComputeHash
        private void ComputeHash_Click(object sender, EventArgs e)
        {
           DeployList = new List<DeploymentFile>();
           foreach (var item in GetListOfFilesInDeployFolder())
               DeployList.Add(new DeploymentFile(item));
           FilesGrid.DataSource = DeployList;
        }

        #endregion ComputeHash

        #region ValidateFileHash
        private void ValidateHash_Click(object sender, EventArgs e)
        {            
            Stopwatch stopwatch = new Stopwatch();
            // Begin timing.
            stopwatch.Start();
            bool Abort = false;
            List<string> filesList = GetListOfFilesInDeployFolder();
            ValidationList = new List<DeploymentFile>();
            foreach (var item in DeployList)
                ValidationList.Add(new DeploymentFile(item.FilePath));

            //If new files are not added or deleted
            for (int i = 0; i < ValidationList.Count; i++)
            {
                if (ValidationList.Count != filesList.Count) Abort = true;
                if (ValidationList[i].FilePath != filesList[i]) Abort = true;
            }
            //if all files are valid and exists in directory
            if (!Abort && ValidationList.Exists((x)=>x.IsFilePathValid==false))
                Abort = true;

            if (Abort)
            {
              //disable message box to calculate accurate execution time through stop watch.
                MessageBox.Show("Files/Folder structure changed or modified since last check");
            }

            if(!Abort)
            {
                for (int i = 0; i < ValidationList.Count; i++)
                    if (ValidationList[i].HashedValue != DeployList[i].HashedValue)
                    {
                        ValidationList[i].IsFileModified = true;
                        Abort = true; 
                    }
            }

            FilesGrid.DataSource = ValidationList;
            
            stopwatch.Stop();
            label1.Text = "Time taken in Validation : " + stopwatch.Elapsed;            
        }

        #endregion
       
        private List<string> GetListOfFilesInDeployFolder()
        {
            filePath = textBox1.Text;
            return Directory.GetFiles(@filePath,"*",SearchOption.AllDirectories).ToList();
        }

        private void FileValidator_Load(object sender, EventArgs e)
        {
            FilesGrid.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.DisplayedCells;
        }               
    }

上面的截图显示了计算和验证哈希值所花费的时间。 如果在点击“计算哈希”按钮和点击“检查修改”按钮之间对某些文件进行了修改,则这些修改将在 IsFilemodified 列中显示。 我还在记录文件结构并将其与文件结构进行比较,文件路径的任何更改都将在 IsFilePathValid 列中显示。

关注点

有趣的是,对于较少的文件,SHA1 和 MD5 算法花费的时间相似。 如果文件数量和文件大小增加,MD5 算法比 SHA1 更有效。 然而,SHA1 在开发人员圈子中更受信任。 我认为 MD5 更好,因为我们在这里并没有真正挑战安全性,我们更关心文件内容的完整性。

使用 MD5 和 SHA1 进行文件哈希比较的快速程序 - CodeProject - 代码之家
© . All rights reserved.