我需要为一个包含一些文件的文件夹创建哈希值。我已经对每个文件完成了此任务,但我正在寻找一种方法来为文件夹中的所有文件创建一个哈希值。有什么想法吗?
(当然,我可以为每个文件创建哈希值并将其连接到一些大哈希值,但这不是我喜欢的方式)
这个程序会对所有文件(相对路径)和内容进行哈希处理,并正确处理文件排序。
而且速度很快——对于一个4MB的目录,只需要30毫秒。
using System;
using System.Text;
using System.Security.Cryptography;
using System.IO;
using System.Linq;
...
public static string CreateMd5ForFolder(string path)
{
// assuming you want to include nested folders
var files = Directory.GetFiles(path, "*", SearchOption.AllDirectories)
.OrderBy(p => p).ToList();
MD5 md5 = MD5.Create();
for(int i = 0; i < files.Count; i++)
{
string file = files[i];
// hash path
string relativePath = file.Substring(path.Length + 1);
byte[] pathBytes = Encoding.UTF8.GetBytes(relativePath.ToLower());
md5.TransformBlock(pathBytes, 0, pathBytes.Length, pathBytes, 0);
// hash contents
byte[] contentBytes = File.ReadAllBytes(file);
if (i == files.Count - 1)
md5.TransformFinalBlock(contentBytes, 0, contentBytes.Length);
else
md5.TransformBlock(contentBytes, 0, contentBytes.Length, contentBytes, 0);
}
return BitConverter.ToString(md5.Hash).Replace("-", "").ToLower();
}
Dunc的回答效果不错,但是它不能处理空目录。下面的代码返回MD5值“d41d8cd98f00b204e9800998ecf8427e”(一个长度为0的字符流的MD5值)用于空目录。
public static string CreateDirectoryMd5(string srcPath)
{
var filePaths = Directory.GetFiles(srcPath, "*", SearchOption.AllDirectories).OrderBy(p => p).ToArray();
using (var md5 = MD5.Create())
{
foreach (var filePath in filePaths)
{
// hash path
byte[] pathBytes = Encoding.UTF8.GetBytes(filePath);
md5.TransformBlock(pathBytes, 0, pathBytes.Length, pathBytes, 0);
// hash contents
byte[] contentBytes = File.ReadAllBytes(filePath);
md5.TransformBlock(contentBytes, 0, contentBytes.Length, contentBytes, 0);
}
//Handles empty filePaths case
md5.TransformFinalBlock(new byte[0], 0, 0);
return BitConverter.ToString(md5.Hash).Replace("-", "").ToLower();
}
}
filePath
截断为相对路径,以创建pathBytes
。 - Stanley.Goldmanpublic static class HashAlgorithmExtensions
{
public static async Task<byte[]> ComputeHashAsync(this HashAlgorithm alg, IEnumerable<FileInfo> files, bool includePaths = true)
{
using (var cs = new CryptoStream(Stream.Null, alg, CryptoStreamMode.Write))
{
foreach (var file in files)
{
if (includePaths)
{
var pathBytes = Encoding.UTF8.GetBytes(file.FullName);
cs.Write(pathBytes, 0, pathBytes.Length);
}
using (var fs = file.OpenRead())
await fs.CopyToAsync(cs);
}
cs.FlushFinalBlock();
}
return alg.Hash;
}
}
一个将文件夹中所有文件进行哈希的示例:
async Task<byte[]> HashFolder(DirectoryInfo folder, string searchPattern = "*", SearchOption searchOption = SearchOption.TopDirectoryOnly)
{
using(var alg = MD5.Create())
return await alg.ComputeHashAsync(folder.EnumerateFiles(searchPattern, searchOption));
}
Path.GetRelativePath(folder, file.FullName)
,这样即使文件位于不同的文件夹位置,哈希值也是稳定的。 - Bjorg> tar cf hashes *.abc
> md5sum hashes
> md5sum *.abc | md5sum
编辑:以上两种方法都不会对文件进行排序,因此可能会因shell扩展星号的方式而在每次调用时返回不同的哈希值。
如果您已经获得所有文件的哈希值,只需按字母顺序排序这些哈希值,将它们连接起来并再次进行哈希以创建超级哈希。
将文件名和文件内容连接成一个大字符串并进行哈希,或者为了提高性能,分块进行哈希。
当然,您需要考虑一些事情:
快速而简单的文件夹哈希,不会深入到子目录或读取二进制数据。它基于文件和子文件夹名称。
Public Function GetFolderHash(ByVal sFolder As String) As String
Dim oFiles As List(Of String) = IO.Directory.GetFiles(sFolder).OrderBy(Function(x) x.Count).ToList()
Dim oFolders As List(Of String) = IO.Directory.GetDirectories(sFolder).OrderBy(Function(x) x.Count).ToList()
oFiles.AddRange(oFolders)
If oFiles.Count = 0 Then
Return ""
End If
Dim oDM5 As System.Security.Cryptography.MD5 = System.Security.Cryptography.MD5.Create()
For i As Integer = 0 To oFiles.Count - 1
Dim sFile As String = oFiles(i)
Dim sRelativePath As String = sFile.Substring(sFolder.Length + 1)
Dim oPathBytes As Byte() = System.Text.Encoding.UTF8.GetBytes(sRelativePath.ToLower())
If i = oFiles.Count - 1 Then
oDM5.TransformFinalBlock(oPathBytes, 0, oPathBytes.Length)
Else
oDM5.TransformBlock(oPathBytes, 0, oPathBytes.Length, oPathBytes, 0)
End If
Next
Return BitConverter.ToString(oDM5.Hash).Replace("-", "").ToLower()
End Function
OrderBy...
)。 - Dunc