Dun*_*unc 30
这将散列所有文件(相对)路径和内容,并正确处理文件排序.
它很快 - 就像一个4MB目录的30ms.
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();
}
Run Code Online (Sandbox Code Playgroud)
小智 13
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();
}
}
Run Code Online (Sandbox Code Playgroud)
创建文件的tarball,散列tarball.
> tar cf hashes *.abc
> md5sum hashesRun Code Online (Sandbox Code Playgroud)
或者将单个文件和管道输出散列为hash命令.
> md5sum *.abc | md5sumRun Code Online (Sandbox Code Playgroud)
编辑:上面的两种方法都不对文件进行排序,因此可能会为每次调用返回不同的哈希值,具体取决于shell如何扩展星号.
这是一个使用流来避免内存和延迟问题的解决方案。
默认情况下,文件路径包含在散列中,这不仅会考虑文件中的数据,还会考虑文件系统条目本身,从而避免散列冲突。这篇文章被标记为security,所以这应该很重要。
最后,此解决方案使您可以控制散列算法、散列哪些文件以及以何种顺序进行散列。
public 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;
}
}
Run Code Online (Sandbox Code Playgroud)
散列文件夹中所有文件的示例:
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));
}
Run Code Online (Sandbox Code Playgroud)
将文件名和文件内容连接在一个大字符串中并对其进行哈希处理,或者以块的形式进行哈希处理以提高性能。
当然,您需要考虑以下几点: