Создание хеша для папки
Мне нужно создать хэш для папки, содержащей некоторые файлы. Я уже выполнил эту задачу для каждого из файлов, но я ищу способ создать один хэш для всех файлов в папке. Любые идеи, как это сделать?
(конечно, я могу создать хэш для каждого файла и связать его с каким-то большим хэшем, но это не способ, который мне нравится)
Спасибо заранее.
Ответы
Ответ 1
Это хеширует все файлы (относительные) пути и содержимое и корректно обрабатывает порядок файлов.
И это быстро - как 30ms для каталога 4MB.
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();
}
Ответ 2
Ответ Dunc работает хорошо; однако он не обрабатывает пустой каталог. Код ниже возвращает MD5 'd41d8cd98f00b204e9800998ecf8427e' (MD5 для потока длины длиной 0) для пустого каталога.
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();
}
}
Ответ 3
Создайте tarball файлов, хэш-архив.
> tar cf hashes *.abc
> md5sum hashes
Или хешировать отдельные файлы и выходные данные в хэш-команду.
> md5sum *.abc | md5sum
Изменить: оба подхода выше не сортируют файлы, поэтому могут возвращать разные хэши для каждого вызова, в зависимости от того, как оболочка расширяет звездочки.
Ответ 4
Объединить имена файлов и файлов в одну большую строку и хэш, или сделать хэширование в кусках для производительности.
Конечно, вам нужно учитывать несколько вещей:
- Вам нужно отсортировать файлы по имени, поэтому вы не получите два разных хэша в случае изменения порядка файлов.
- Используя этот метод, вы учитываете только имена файлов и контент. если имя файла не засчитывается, вы можете сортировать по контенту сначала, а затем хеш, если имеет значение больше атрибутов (ctime/mtime/hidden/archived..), включите их в строку, которая будет хеширована.
Ответ 5
Если у вас уже есть хеши для всех файлов, просто сортируйте хеши в алфавитном порядке, объедините их и снова хэш их, чтобы создать хэш uber.
Ответ 6
Здесь решение, которое использует потоковую передачу, чтобы избежать проблем с памятью и задержками.
По умолчанию пути к файлам включаются в хеширование, что будет учитывать не только данные в файлах, но и сами записи файловой системы, что позволяет избежать коллизий хешей. Этот пост помечен как 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;
}
}
Пример, который хэширует все файлы в папке:
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));
}