【问题标题】:How to hash a single file multiple ways at same time?如何同时以多种方式散列单个文件?
【发布时间】:2012-04-26 15:26:21
【问题描述】:

我正在尝试设计一个简单的应用程序来计算文件的 CRC32/md5/sha1/sha256/sha384/sha512,但我遇到了一些障碍。这是在 C# 中完成的。

我希望能够尽可能高效地做到这一点,所以我最初的想法是在处理之前先将文件读入内存流,但我很快发现非常大的文件会导致我非常耗尽内存迅速地。所以看来我必须改用文件流。在我看来,问题在于一次只能运行一个哈希函数,并且使用文件流这样做需要一段时间才能完成每个哈希。

我怎样才能将文件的一小部分读入内存,用所有 6 种算法对其进行处理,然后再进入另一个块……或者散列不是那样工作的?

这是我最初尝试将文件读入内存。当我在内存流上运行散列算法之前尝试将 CD 映像读入内存时失败:

    private void ReadToEndOfFile(string filename)
    {
        if (File.Exists(filename))
        {
            FileInfo fi = new FileInfo(filename);
            FileStream fs = new FileStream(filename, FileMode.Open, FileAccess.Read);
            byte[] buffer = new byte[16 * 1024];

            //double step = Math.Floor((double)fi.Length / (double)100);

            this.toolStripStatusLabel1.Text = "Reading File...";
            this.toolStripProgressBar1.Maximum = (int)(fs.Length / buffer.Length);
            this.toolStripProgressBar1.Value = 0;

            using (MemoryStream ms = new MemoryStream())
            {
                int read;
                while ((read = fs.Read(buffer, 0, buffer.Length)) > 0)
                {
                    ms.Write(buffer, 0, read);
                    this.toolStripProgressBar1.Value += 1;
                }

                _ms = ms;
            }
        }
    }

【问题讨论】:

    标签: c# cryptography hash


    【解决方案1】:

    您已经完成了大部分工作,只是不需要一次将整个内容读入内存。

    .Net 中的所有哈希都派生自 HashAlgorithm 类。这有两种方法:TransformBlockTransformFinalBlock。因此,您应该能够为您的文件读取一个块,将其填充到您要使用的任何散列的 TransformBlock 方法中,然后进入下一个块。只需记住为文件中的最后一个块调用 TransformFinalBlock,因为这会得到包含哈希的字节数组。

    现在,我一次只做一个哈希,直到它工作,然后担心同时运行哈希(使用任务并行库之类的东西)

    【讨论】:

    • 我已经尝试使用 MD5 让它工作,并且程序运行,尽管它似乎生成了不正确的哈希值。这是我的代码的链接:link
    • 你应该在调用TransformFinalBlock时使用read而不是buffer.Length
    • 非常感谢!昨晚我为此苦恼了一段时间。最后拼凑了一些愚蠢的东西来让它工作,但我不禁觉得这是不必要的。我发现这是因为最后一个数组被完全读取,即使最后一个块太小了。我最终让它为最后一块创建一个新的字节数组,使其等于最后一块的大小。
    【解决方案2】:

    哈希算法的设计方式可以让您逐步计算哈希值。您可以找到该 here 的 C#/.NET 示例。您可以轻松修改提供的代码以在每个步骤中更新多个哈希算法实例。

    【讨论】:

      【解决方案3】:

      这可能是让您了解 TPL 数据流对象的好机会。在一个线程中读取文件并将数据发布到BroadcastBlock<T>BroadcastBlock<T> 将链接到 6 个不同的 ActionBlock<T> 实例。每个ActionBlock<T> 将对应您的 6 个哈希策略之一。

      var broadcast = new BroadcastBlock<byte[]>(x => x);
      
      var strategy1 = new ActionBlock<byte[]>(input => DoHash(input, SHA1.Create()));
      var strategy2 = new ActionBlock<byte[]>(input => DoHash(input, MD5.Create()));
      // Create the other 4 strategies.
      
      broadcast.LinkTo(strategy1);
      broadcast.LinkTo(strategy2);
      // Link the other 4.
      
      using (var fs = File.Open(@"yourfile.txt", FileMode.Open, FileAccess.Read))
      using (var br = new BinaryReader(fs))
      {
        while (br.PeekChar() != -1)
        {
          broadcast.Post(br.ReadBytes(1024 * 16));
        }
      }
      

      BroadcastBlock&lt;T&gt; 会将每个数据块转发到所有链接的ActionBlock&lt;T&gt; 实例。

      由于您的问题更多地集中在如何让这一切同时发生,我将把 DoHash 的实施留给您。

      private void DoHash(byte[] input, HashAlgorithm algorithm)
      {
        // You will need to implement this.
      }
      

      【讨论】:

      • 这看起来是一种非常有趣的多线程方法。可惜它在.net 4.5中。无论出于何种原因,我都很难说服自己使用 .net 4.0,因为我觉得它还不够主流。
      • 看来这种方法行不通。将为每个输入的字节数组调用 DoHash。它们应该如何结合?
      • BroadcastBlock 如果缓冲区已满,则丢弃消息。如果文件读取速度高于散列速度,则会导致错误的散列计算。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-10-17
      • 1970-01-01
      • 2023-03-26
      • 1970-01-01
      相关资源
      最近更新 更多