未在 Azure Blob 存储 v12 中计算 ContentHash

Posted

技术标签:

【中文标题】未在 Azure Blob 存储 v12 中计算 ContentHash【英文标题】:ContentHash not calculated in Azure Blob Storage v12 【发布时间】:2020-11-30 18:06:43 【问题描述】:

继续传奇,这是第一部分:ContentHash is null in Azure.Storage.Blobs v12.x.x

经过大量调试,根本原因似乎是 上传 blob 后未计算内容哈希,因此 BlobContentInfoBlobProperties 返回一个空内容哈希,而我的整个流程基于从 Azure 接收哈希。

我发现这取决于我调用哪个 HttpRequest 流方法并上传到 azure:

HttpRequest.GetBufferlessInputStream(),不计算内容hash,即使我进入azure storage explorer,blob的ContentMD5也是空的。

HttpRequest.InputStream() 一切正常。


你知道为什么会有这种不同的行为吗?你知道如何为GetBufferlessInputStream 方法接收的流接收内容哈希吗?

所以代码流程是这样的:

var stream = HttpContext.Current.Request.GetBufferlessInputStream(disableMaxRequestLength: true)

var container = _blobServiceClient.GetBlobContainerClient(containerName);
var blob = container.GetBlockBlobClient(blobPath);

BlobHttpHeaders blobHttpHeaders = null;
if (!string.IsNullOrWhiteSpace(fileContentType))

     blobHttpHeaders = new BlobHttpHeaders()
     
          ContentType = fileContentType,
     ;


// retry already configured of Azure Storage API
await blob.UploadAsync(stream, httpHeaders: blobHttpHeaders);

return await blob.GetPropertiesAsync();

在上面的代码 sn-p ContentHash 中没有计算,但是如果我改变从 http 请求中获取流的方式,则计算以下 sn-p ContentHash

var stream = HttpContext.Current.Request.InputStream

附:我认为这很明显,但是对于旧的 sdk,内容哈希是针对 GetBufferlessInputStream 方法接收的流计算的

P.S2 你也可以在 github 上找到一个未解决的问题:https://github.com/Azure/azure-sdk-for-net/issues/14037

P.S3 添加了代码片段

【问题讨论】:

你好,关于这个问题你还有什么问题吗? @IvanYang 我对您的解决方法进行了快速测试。现在我正在做一些性能测试,看看是如何受到影响的。 请稍后提供任何反馈:) 您好,有什么反馈吗? @IvanYang 目前我将进一步了解您提出的解决方案。谢谢! 【参考方案1】:

今天遇到了这个。从我的挖掘来看,这似乎是您用来上传的Stream 类型的症状,它并不是真正的错误。为了为您的 blob 生成散列(在上传之前在客户端完成),它需要读取流。这意味着它需要在生成哈希后将流的位置重置回 0(对于实际的上传过程)。这样做需要能够对流执行 Seek 操作。如果您的流不支持 Seek,那么它似乎不会生成哈希。

要解决此问题,请确保您提供的流支持 Seek (CanSeek)。如果不是,则使用不同的流/将您的数据复制到一个流中(例如MemoryStream)。另一种方法是让 Blob SDK 的内部为您执行此操作。

【讨论】:

嗯 - 我不确定这就是全部。我刚刚在我的一个容器中发现了这个问题。所有文件都使用完全相同的机制(使用 MemoryStream)写入,只有一些文件缺少哈希。 看起来 blob 的大小可能是一个影响因素 - 小 blob( 您可能会遇到不同的问题。我没有注意到你在描述什么。就我而言,在考虑任务完成之前,我实际上验证了来自 Azure 的哈希与我的本地哈希匹配,并且我没有遇到像您遇到的任何问题? 我在github.com/Azure/azure-sdk-for-net/issues/17676 发布了一个问题。就我而言,根本原因似乎是我正在使用 StorageTransferOptions 来优化上传时间。这会导致 SDK 对较大的文件使用不同的上传机制,这会阻止服务器计算/添加校验和。结果是客户端可能不应该依赖服务器添加校验和,如果需要,应该始终使用 BlobUploadOptions.HttpHeaders.ContentHash 显式添加它。 感谢您的跟进! 是的,对于任何类型的块(大)上传,它都不会为您计算“全局 MD5”:***.com/a/69319211/32453 我假设在这种情况下,因为它是从输入流上传的,所以它不会知道它会“有多大”,所以上传它就好像它很大......因此不会生成 MD5。并且还同意在这种情况下,至少在客户端中有一个选项来始终设置 MD5 以保持一致性会很好......【参考方案2】:

解决方法是通过GetBufferlessInputStream()方法获取流时,将其转换为MemoryStream,然后上传MemoryStream。然后它可以生成contenthash。示例代码如下:

        var stream111 = System.Web.HttpContext.Current.Request.GetBufferlessInputStream(disableMaxRequestLength: true);
        //convert to memoryStream.
        MemoryStream stream = new MemoryStream();
        stream111.CopyTo(stream);
        stream.Position = 0;

        //other code
        // retry already configured of Azure Storage API
        await blob.UploadAsync(stream, httpHeaders: blobHttpHeaders);

不知道为什么,但是根据我的调试,我可以看到在最新的SDK中使用GetBufferlessInputStream()方法时,在上传过程中,它实际上调用了后端的Put Block api。并且在这个api中,MD5哈希不与blob一起存储(详见here)。截图如下:

但是,当使用InputStream 时,它会调用Put Blob api。截图如下:

【讨论】:

我明白了。我将使用 MemoryStream 进行一些性能测试。 Microsoft 很好地描述了我想使用 GetBufferlessStream 的主要原因:“InputStream 属性等到收到整个请求后才返回 Stream 对象。相反,GetBufferlessInputStream 方法立即返回 Stream 对象。您可以使用在接收到正文的完整内容之前开始处理实体正文的方法。"

以上是关于未在 Azure Blob 存储 v12 中计算 ContentHash的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 .NET v12 SDK 在 Azure Blob 存储中上传具有指定 ContentType 的 Blob?

与 v11 相比,使用 v12 将文件上传到 Azure Blob 存储 SDK 的 ASP.NET Core 使用更高的内存

上传大文件 Azure Blob .net SDK v12 问题

使用 Azure Java SDK V12 和 ListBlobs() 在 Azure Blobstorage 中列出 Blob 非常慢

如何从适用于 Node.js 的 Azure blob v12 SDK 中删除 blob

SSDT 项目未在 azure v12 数据库的 Visual Studio 2012 中加载