c# - 报告哈希进度

标签 c# .net winforms hash md5

我正在通过下面提供的代码学习文件的 MD5 哈希值。但是,随着文件大小的增长,计算也需要很长时间。我想在进度条对象上反射(reflect)这个计算,但我不知道。

我想要这样的东西;

progressBar.Value = mD5.ComputedBytes;
progressBar.Maximum = mD5.TotalBytesToCompute;

这是怎么做到的?

代码;

public static string getMD5HashFromFile(string fileName)
{
    string str = "";
    using (MD5 mD5 = MD5.Create())
    {
        using (FileStream fileStream = File.OpenRead(fileName))
        { str = BitConverter.ToString(mD5.ComputeHash(fileStream)).Replace("-", string.Empty); fileStream.Close(); }
    }
    return str;
}

最佳答案

HashAlgorithm 使您能够使用 TransformBlock 散列数据 block 和 TransformFinalBlock 方法。另一方面, Stream 类还允许您异步读取数据 block 。

考虑到这些事实,您可以创建一种方法来获取流作为输入,然后分块读取流,然后针对每个 chuck 对其进行哈希处理,并通过计算读取的字节数来报告进度(处理的字节数)。

ComputeHashAsync

我在这里创建了一个 ComputeHashAsync HashAlgorithm 的扩展方法类(class)。它接受:

  • 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 - 更新 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();
}

<子> 注意:以 block 为单位计算哈希的实现,摘自博客post通过 Alexandre Gomes,然后我更改了代码使其成为 async并支持CancellationTokenIProgress<long> .

关于c# - 报告哈希进度,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53965380/

相关文章:

c# - 如何找到数组中每个元素的最近值?

.net - 在终结器中访问引用类型成员变量是否安全?

c# - 返回中间字母或回文字母的函数

c# - ACRA 类似于 C# .NET 的崩溃报告库

c# - 无法使用 Visual Studio 2010 生成的 DLL 创建类型库文件,但可以使用命令行编译

c# - 跳过潜在的空值 c#

.net - 使用 POI 将 DOC 导出为 PDF

c# - 在 Winform 中访问用户控件的控件属性的正确方法是什么?

c# - c sharp exe打开时应该要求 "run as administrator"提示

c# - 使用 SqlBulkCopy 将 CSV 文件导入 SQL Server