异步文件哈希和磁盘写入如何实际工作?

ahs*_*ele 5 c# md5 filestream async-await asp.net-core

我正在构建一个ASP.NET核心应用程序,它需要处理大量文件上传 - 高达200GB.我的目标是将这些文件写入磁盘并同时捕获MD5哈希.

我已经完成并创建了自己的方法来识别来自HTTP客户端请求的文件流,如上传带有流的大文件中所述.一旦我找到了流,我使用下面的代码写入磁盘并创建MD5哈希.

// removed the curly brackets from using statements for readability on Stack Overflow
var md5 = MD5.Create();
using (var targetStream = File.OpenWrite(pathAndFileName))
using (var cryptoStream = new CryptoStream(targetStream, md5, CryptoStreamMode.Write))
using (var sourceStream = fileNameAndStream.FileStream)
{
    await sourceStream.CopyToAsync(cryptoStream);
}

var hash = md5.Hash;
md5.Dispose();
Run Code Online (Sandbox Code Playgroud)

什么是令人敬畏的是上述工作(文件创建和哈希生成).什么不是那么棒,我不完全理解这是如何工作的:

  • cryptoStream被复制,然后写入targetStream
  • 是将cryptoStream字节保存在内存中还是只是随着它们读取它们?
  • cryptoStreamtargetStream异步发生?
  • 或者它是一个异步拷贝cryptoStream和同步写入targetStream

我很高兴这有效,但如果没有完全理解它,我担心我引入了一些邪恶的东西.

Evk*_*Evk 6

它的工作原理如下:

1)CopyToAsync分配指定大小的字节缓冲区(如果使用像问题那样的重载,则使用默认大小).然后它调用ReadAsync源流来填充该缓冲区,然后调用WriteAsync目标流将该缓冲区写入目标流.重复,直到写入所有数据.因此,此操作在内存中保存小字节数组(缓冲区).读写是异步的(如果源\目标流支持).

2)CryptoStream在写模式下的工作原理是这样的:当你写它,它需要缓冲你写(这是与上述相同的缓冲液),并且将其以ICryptoTransformimplementaiton你传递给它(在这种情况下- MD5).转换可能需要以特定大小的块(由ICryptoTransform.InputBlockSize属性确定)进行处理.在这种情况下,CryptoStream可能会缓存您写入的数据,直到存在特定大小的完整块.这不是问题,因为这些块通常非常小(远小于合理的缓冲区大小CopyAsync).然后它将ICryptoTransform.TransformBlock逐个传递这些块,并接收输出(另一个字节数组).这个过程是同步的,因为这里没有什么可以是异步的.

3)块转换后ICryptoTransform- 将此块targetStream异步(使用WriteAsync)写入输出流(在本例中).因此内存消耗CryptoStream也很小,并且与目标转换输入和输出块大小有关.

4)MD5实现ICryptoTransform使用传递块来连续计算哈希,因为该算法不需要立即存在完整数据来计算哈希值,它可以逐块计算它.然后输出与输入端接收的完全相同的块,因此不会进行转换.这意味着TransformBlockMD5只是按原样返回输入,同时在内部更新哈希.

总结并回答您的问题:

  • 加密流只保存小缓冲区来缓冲数据直到变换输入块大小,它会尽快将转换后的数据写入输出流.它不包含整个数据的副本.
  • 在加密流本身没有IO工作,它只执行CPU绑定工作(转换),这是同步发生的,应该如此.但是当你写入加密流时 - 它写入目标流 - 并且确实是异步发生的.

附注 - 要真正利用异步文件IO - 您需要使用"异步"选项初始化文件流,例如:

new FileStream(pathAndFileName, FileMode.Create, FileAccess.Write, FileShare.None,
               4096, FileOptions.Asynchronous)
Run Code Online (Sandbox Code Playgroud)

否则,即使WriteAsync使用,您对目标流的写入也将是同步的.