HashAlgorithm 使您能够使用TransformBlock 和TransformFinalBlock 方法在块中散列数据。另一方面,Stream 类还允许您异步读取块中的数据。
考虑到这些事实,您可以创建一个方法来获取流作为输入,然后以块的形式读取流,然后为每个卡盘散列它并通过计算读取的字节数来报告进度(处理的字节数)。
ComputeHashAsync
在这里,我为HashAlgorithm 类创建了一个ComputeHashAsync 扩展方法。它接受:
-
stream:输入Stream 计算哈希。
-
cancellationToken:可选的CancellationToken,可用于取消操作
-
progress:IProgress<long> 的可选实例,用于接收进度报告(处理的字节数)。
-
buggerSize:用于读取数据的可选缓冲区大小。默认 id 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 - 更新进度条
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();
}
注意: 以块计算哈希的实现,取自 Alexandre Gomes 的博客post,然后我将代码改为async 并支持@987654349 @ 和 IProgress<long>。