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

时间:2010-01-23 19:51:48

标签: c# .net-3.5 hash md5 buffer

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

    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 Framework中找到任何覆盖,我在做什么?它从来没有存在过,或者我只是在搜索时很糟糕?同时进行写入和校验和计算的原因是因为大文件有意义。

5 个答案:

答案 0 :(得分:49)

我喜欢上面的答案,但为了完整起见,并且是一个更通用的解决方案,请参阅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知道它已完成)。

答案 1 :(得分:46)

您使用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以完成该过程。

答案 2 :(得分:4)

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

答案 3 :(得分:3)

希望哈希算法能够处理这种情况,通常用3个函数实现:

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

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

答案 4 :(得分:3)

我只需要做类似的事情,但想要异步读取文件。它使用的是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);
   }
}