首页 文章

可以使用缓冲读取计算MD5(或其他)哈希值吗?

提问于
浏览
33

我需要计算相当大的文件(千兆字节)的校验和 . 这可以使用以下方法完成:

private byte[] calcHash(string file)
    {
        System.Security.Cryptography.HashAlgorithm ha = System.Security.Cryptography.MD5.Create();
        FileStream fs = new FileStream(file, FileMode.Open, FileAccess.Read);
        byte[] hash = ha.ComputeHash(fs);
        fs.Close();
        return hash;
    }

但是,文件通常是以缓冲方式预先写入的(比如一次写入32mb) . 我确信我看到了一个覆盖哈希函数,它允许我在写入的同时计算MD5(或其他)哈希,即:计算一个缓冲区的哈希值,然后将得到的哈希值输入到下一个迭代中 .

像这样的东西:(伪代码)

byte [] hash = new byte [] { 0,0,0,0,0,0,0,0 };
while(!eof)
{
   buffer = readFromSourceFile();
   writefile(buffer);
   hash = calchash(buffer, hash);
}

hash现在通过在整个文件上运行calcHash函数来实现 .

现在,我无法在.Net 3.5框架中找到任何覆盖,我在做什么?它从来没有存在过,或者我只是在搜索时很糟糕?同时进行写入和校验和计算的原因是因为大文件才有意义 .

5 回答

  • 49

    您使用 TransformBlockTransformFinalBlock 方法以块的形式处理数据 .

    // Init
    MD5 md5 = MD5.Create();
    int offset = 0;
    
    // For each block:
    offset += md5.TransformBlock(block, 0, block.Length, block, 0);
    
    // For last block:
    md5.TransformFinalBlock(block, 0, block.Length);
    
    // Get the has code
    byte[] hash = md5.Hash;
    

    注意:它(至少与MD5提供程序一样)可以将所有块发送到 TransformBlock ,然后将空块发送到 TransformFinalBlock 以完成该过程 .

  • 3

    我喜欢上面的答案,但为了完整起见,并且是一个更通用的解决方案,请参阅 CryptoStream 类 . 如果您已经在处理流,则很容易将流包装在 CryptoStream 中,并将 HashAlgorithm 作为 ICryptoTransform 参数传递 .

    var file = new FileStream("foo.txt", FileMode.Open, FileAccess.Write);
    var md5 = MD5.Create();
    var cs = new CryptoStream(file, md5, CryptoStreamMode.Write);
    while (notDoneYet)
    {
        buffer = Get32MB();
        cs.Write(buffer, 0, buffer.Length);
    }
    System.Console.WriteLine(BitConverter.ToString(md5.Hash));
    

    您可能必须在获取哈希值之前关闭流(因此 HashAlgorithm 知道它已完成) .

  • 4

    似乎你可以使用 TransformBlock / TransformFinalBlock ,如下例所示:Displaying progress updates when hashing large files

  • 2

    Hash算法有望处理这种情况,通常用3个函数实现:

    hash_init() - 调用分配资源并开始哈希 .
    hash_update() - 在新数据到达时调用 .
    hash_final() - 完成计算和免费资源 .

    查看http://www.openssl.org/docs/crypto/md5.htmlhttp://www.openssl.org/docs/crypto/sha.html,了解C中的标准示例;我相信你的平台有类似的库 .

  • 46

    我只需要做类似的事情,但想要异步读取文件 . 它正在使用TransformBlock和TransformFinalBlock,并给我与Azure一致的答案,所以我认为这是正确的!

    private static async Task<string> CalculateMD5Async(string fullFileName)
    {
      var block = ArrayPool<byte>.Shared.Rent(8192);
      try
      {
         using (var md5 = MD5.Create())
         {
             using (var stream = new FileStream(fullFileName, FileMode.Open, FileAccess.Read, FileShare.Read, 8192, true))
             {
                int length;
                while ((length = await stream.ReadAsync(block, 0, block.Length).ConfigureAwait(false)) > 0)
                {
                   md5.TransformBlock(block, 0, length, null, 0);
                }
                md5.TransformFinalBlock(block, 0, 0);
             }
             var hash = md5.Hash;
             return Convert.ToBase64String(hash);
          }
       }
       finally
       {
          ArrayPool<byte>.Shared.Return(block);
       }
    }
    

相关问题