【问题标题】:Report Hash Progress报告哈希进度
【发布时间】:2019-05-26 16:24:06
【问题描述】:

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

我想要这样的东西;

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;
}

【问题讨论】:

  • 值得注意的是(来自 MD5 Wikipedia 文章:en.wikipedia.org/wiki/MD5“CMU 软件工程研究所认为 MD5 本质上是'密码损坏且不适合进一步使用'”。您将无法显示“进度”,除非您可以将操作分成多个块(例如,如果您正在散列 5 个文件,您可以在每个文件之后显示 20% 的进度)。你可以弹出一个不定的进度条来显示工作正在进行,但是你真的不能显示具体的进度
  • HashAlgorithm 使您能够使用TransformBlockTransformFinalBlock 方法以块的形式散列数据。另一方面,Stream 类还允许您异步读取块中的数据。考虑到这些事实,您可以创建一个方法来获取流作为输入,然后以块的形式读取流,然后为每个卡盘散列它并通过计算读取的字节数来报告进度(例如百分比)。
  • 解决方案不限于MD5,也可以用于其他哈希算法。

标签: c# .net winforms hash md5


【解决方案1】:

HashAlgorithm 使您能够使用TransformBlockTransformFinalBlock 方法在块中散列数据。另一方面,Stream 类还允许您异步读取块中的数据。

考虑到这些事实,您可以创建一个方法来获取流作为输入,然后以块的形式读取流,然后为每个卡盘散列它并通过计算读取的字节数来报告进度(处理的字节数)。

ComputeHashAsync

在这里,我为HashAlgorithm 类创建了一个ComputeHashAsync 扩展方法。它接受:

  • stream:输入Stream 计算哈希。
  • cancellationToken:可选的CancellationToken,可用于取消操作
  • progressIProgress<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&lt;long&gt;

【讨论】:

  • 您好,首先非常感谢您的回复。我不确定如何使用此代码。你能帮我一点吗?我为第一个代码创建了一个名为“Hash.cs”的类,并将代码行放在类中,但有些行似乎不正确。
  • 嗨,不客气。关于使用代码,只需创建一个空的.cs 代码文件,然后将我的代码复制到文件中即可。那么一切都应该没问题,无论你在哪里创建像 md5 这样的哈希算法实例,它都会有一个 ComputeHashAsync 方法。
  • 这就是Extension Methods 在 C# 中的工作方式。
  • 我创建了“Hash.cs”文件并复制了您的代码。然后,出现以下错误。 1:ibb.co/wNdqV36 2:ibb.co/J7jNj2c 3:ibb.co/4NRXZ22 如何修复这些错误?
  • 您好!我不确定,但是将fs.Length 传递给Invoke 并不是一个好主意,因为它可能在fs 关闭时被访问。相反,将它放在一个变量中并传递变量。
猜你喜欢
  • 2013-02-09
  • 1970-01-01
  • 2020-07-04
  • 1970-01-01
  • 2017-01-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-07-05
相关资源
最近更新 更多