HashAlgorithm
дает вам возможность хэшировать данные кусками, используя методы TransformBlock
и TransformFinalBlock
.С другой стороны, класс Stream
также позволяет асинхронно читать данные порциями.
Принимая во внимание эти факты, вы можете создать метод для получения потока в качестве входных данных, а затем для чтения потока в чанках, а затем для каждого хака в хаке и для отчета о ходе выполнения (число процессов в байтах) путем вычисления прочитанных байтов.
ComputeHashAsync
Здесь я создал ComputeHashAsync
метод расширения для HashAlgorithm
класса.Он принимает:
stream
: ввод Stream
для вычисления хэша. cancellationToken
: необязательный CancellationToken
, который можно использовать для отмены операции progress
: необязательный экземпляр IProgress<long>
, который получает отчет о ходе выполнения (количество обработанных байтов). buggerSize
: необязательный размер буфера для чтения данных.Идентификатор по умолчанию 1024 * 1024 байта.
Вот код:
using System;
using System.IO;
using System.Security.Cryptography;
using System.Threading;
using System.Threading.Tasks;
public static class HashAlgorithmExtensions {
public static async Task<byte[]> ComputeHashAsync(
this HashAlgorithm hashAlgorithm, Stream stream,
CancellationToken cancellationToken = default(CancellationToken),
IProgress<long> progress = null,
int bufferSize = 1024 * 1024) {
byte[] readAheadBuffer, buffer, hash;
int readAheadBytesRead, bytesRead;
long size, totalBytesRead = 0;
size = stream.Length;
readAheadBuffer = new byte[bufferSize];
readAheadBytesRead = await stream.ReadAsync(readAheadBuffer, 0,
readAheadBuffer.Length, cancellationToken);
totalBytesRead += readAheadBytesRead;
do {
bytesRead = readAheadBytesRead;
buffer = readAheadBuffer;
readAheadBuffer = new byte[bufferSize];
readAheadBytesRead = await stream.ReadAsync(readAheadBuffer, 0,
readAheadBuffer.Length, cancellationToken);
totalBytesRead += readAheadBytesRead;
if (readAheadBytesRead == 0)
hashAlgorithm.TransformFinalBlock(buffer, 0, bytesRead);
else
hashAlgorithm.TransformBlock(buffer, 0, bytesRead, buffer, 0);
if (progress != null)
progress.Report(totalBytesRead);
if (cancellationToken.IsCancellationRequested)
cancellationToken.ThrowIfCancellationRequested();
} while (readAheadBytesRead != 0);
return hash = hashAlgorithm.Hash;
}
}
Пример 1 - Обновление ProgressBar
byte[] bytes;
using (var hash = MD5.Create())
{
using (var fs = new FileStream(f, FileMode.Open))
{
bytes = await hash.ComputeHashAsync(fs,
progress: new Progress<long>(i =>
{
progressBar1.Invoke(new Action(() =>
{
progressBar1.Value = i;
}));
}));
MessageBox.Show(BitConverter.ToString(bytes).Replace("-", string.Empty));
}
}
Пример 2 - Отмена задачи через 1 секунду
try
{
var s = new CancellationTokenSource();
s.CancelAfter(1000);
byte[] bytes;
using (var hash = MD5.Create())
{
using (var fs = new FileStream(f, FileMode.Open))
{
bytes = await hash.ComputeHashAsync(fs,
cancellationToken: s.Token,
progress: new Progress<long>(i =>
{
progressBar1.Invoke(new Action(() =>
{
progressBar1.Value = i;
}));
}));
MessageBox.Show(BitConverter.ToString(bytes).Replace("-", string.Empty));
}
}
}
catch (OperationCanceledException)
{
MessageBox.Show("Operation canceled.");
}
Создание большого файла для теста
var f = Path.Combine(Application.StartupPath, "temp.log");
File.Delete(f);
using (var fs = new FileStream(f, FileMode.Create))
{
fs.Seek(1L * 1024 * 1024 * 1024, SeekOrigin.Begin);
fs.WriteByte(0);
fs.Close();
}
Примечание: Реализация вычислительного хэша в чанках взята из блога post Александра Гомеса, затем я изменил код, чтобы сделать его async
и поддержкой CancellationToken
и IProgress<long>
.