C# 是否可以使用缓冲读取计算MD5(或其他)哈希?

C# 是否可以使用缓冲读取计算MD5(或其他)哈希?,c#,.net-3.5,hash,md5,buffer,C#,.net 3.5,Hash,Md5,Buffer,我需要计算相当大的文件(GB)的校验和。这可以使用以下方法完成: private byte[] calcHash(string file) { System.Security.Cryptography.HashAlgorithm ha = System.Security.Cryptography.MD5.Create(); FileStream fs = new FileStream(file, FileMode.Open, FileAccess.

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

    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(或其他)散列,即:计算一个缓冲区的散列,然后将结果散列输入下一次迭代

类似这样的:(伪代码ish)

哈希现在与在整个文件上运行calcHash函数所能实现的非常相似


现在,我在.NET3.5框架中找不到任何这样的覆盖,我是在做梦吗?它从未存在过,还是我只是不善于寻找?同时执行写入和校验和计算的原因是,由于文件很大,所以这很有意义

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

hash\u init()
-调用以分配资源并开始哈希。
hash\u update()
-在新数据到达时调用。
hash_final()
-完成计算并释放资源。


看一看C语言中的好的、标准的例子;我相信您的平台也有类似的库。

您可以使用
TransformBlock
TransformFinalBlock
方法对数据进行分块处理

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

注意:它可以将所有块发送到
TransformBlock
,然后将空块发送到
TransformFinalBlock
,以完成流程。

似乎您可以使用
TransformBlock
/
TransformFinalBlock
,如本示例所示:

我喜欢上面的答案,但为了完整性和更一般的解决方案,请参考
CryptoStream
类。如果您已经在处理流,则很容易将流包装在
加密流
中,将
哈希算法
作为
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));

在获取散列之前,您可能必须关闭流(因此
散列算法
知道它已经完成)。

我刚刚不得不做一些类似的事情,但希望异步读取文件。它使用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);
   }
}
专用静态异步任务CalculateMD5Async(字符串fullFileName)
{
var block=ArrayPool.Shared.Rent(8192);
尝试
{
使用(var md5=md5.Create())
{
使用(var stream=newfilestream(fullFileName,FileMode.Open,FileAccess.Read,FileShare.Read,8192,true))
{
整数长度;
while((length=await stream.ReadAsync(block,0,block.length).ConfigureAwait(false))>0)
{
md5.TransformBlock(block,0,length,null,0);
}
md5.TransformFinalBlock(块,0,0);
}
var hash=md5.hash;
返回Convert.tobase64字符串(散列);
}
}
最后
{
ArrayPool.Shared.Return(块);
}
}

答案很好,但问题的“它在.net中的位置?”部分仍然悬而未决。@Pascal:请参阅下面的两个好答案,这两个答案都是在您发表评论之前发布的。哎,caramba!就在那里!这就是我正在寻找的功能。很高兴知道这一切都不是我编造的。感谢Guffa和Rubens及时提供了正确答案+1对于你们两个,我将接受这个答案,因为包含了代码示例。请注意,在调用TransformBlock时,您可以将
block
的第二个实例等效地替换为
null
;实际上,您并不希望发生任何复制;输出参数实际上没有对哈希做任何操作。此外,TransformFinalBlock的长度可以为零。是否可以转换前X个数据块,转储状态数据,然后在恢复新计算的状态后继续下一个数据块?。在云解决方案中有100GB的文件,能够不必一次过检查漏洞文件就好了。机器可以回收ect。@pksorensen:我不这么认为,我没有看到任何获取或设置MD5对象的计算状态的方法或属性。理论上,这当然是可能的,但您可能需要使用该算法的单独实现,以便添加处理状态的方法。该链接已失效,请尝试以下操作:What's
ArrayPool
?确定:需要安装程序包。
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);
   }
}