【问题标题】:OpenReadAsync on CloudBlob returns empty streamCloudBlob 上的 OpenReadAsync 返回空流
【发布时间】:2020-12-15 11:21:07
【问题描述】:

我有一个 azure 函数(EventGrid 触发器),当在 blob 存储(Data Lake Storage Gen2)中创建新的 blob 时会触发该函数。 在这个函数中,我需要读取 blob 并将其反序列化为一个对象(类型已知)。

这是我用来获取 CloudBlob 的方法:

public async Task<CloudBlob> GetCloudBlob(Uri uri)
{
   var cloudBlockBlob = new CloudBlob(uri, _storageCredentials);
   if (await cloudBlockBlob.ExistsAsync())
       return cloudBlockBlob;
   return null;
}

通过这段代码,我读取了云 blob

using var stream = await cloudBlob.OpenReadAsync();
await _requestFileHandler.HandleFile(stream, name, prefix, cloudBlob.Uri);

在 HandleFile 方法中我调用了 DeserializeAsync 方法:

var model = await JsonSerializer.DeserializeAsync<RequestModel>(stream);

由于某种原因,流有时为空(stream.Length 为 0)导致此异常。

The input does not contain any JSON tokens. Expected the input to start with a valid JSON token, when isFinalBlock is true. Path: $ | LineNumber: 0 | BytePositionInLine: 0.

有趣的是,如果我再次重试此 blob 的过程,则流会按预期填充。

我是否缺少任何等待条件,或者 Blob 仍未完全写入存储?

【问题讨论】:

  • await cloudBlockBlob.ExistsAsync() 转换为await cloudBlockBlob.ExistsAsync().ConfigureAwait(false)await cloudBlob.OpenReadAsync() 转换为await cloudBlob.OpenReadAsync().ConfigureAwait(false)await _requestFileHandler.HandleFile(stream, name, prefix, cloudBlob.Uri) 转换为await _requestFileHandler.HandleFile(stream, name, prefix, cloudBlob.Uri).ConfigureAwait(false)await JsonSerializer.DeserializeAsync&lt;RequestModel&gt;(stream) 转换为await JsonSerializer.DeserializeAsync&lt;RequestModel&gt;(stream).ConfigureAwait(false)。它应该很可能工作。
  • @HarshitaSingh-MSFT 为什么要这么做?
  • 你可以阅读这篇文章来深入了解-medium.com/bynder-tech/…
  • 当应用await 关键字时,它会暂停调用方法并将控制权交还给调用者,直到等待的任务完成。我们可以避免强制延续/回调回到原始上下文,我们使用ConfigureAwait(false) 来做到这一点。不使用ConfigureAwait(false) 会导致性能问题和不可靠性。由于您没有使用它,stream 有时在调用者上下文中,有时在当前上下文中。当前上下文中的一个为您提供正确的结果。您可以访问devblogs.microsoft.com/dotnet/configureawait-faq了解更多信息。
  • 如果这回答了您的问题,请告诉我,我会将其作为答案发布。

标签: azure azure-functions azure-blob-storage azure-eventgrid azure-data-lake-gen2


【解决方案1】:

对流进行序列化使流位于末尾,因此当您对其进行反序列化时,没有什么可反序列化的。

你需要添加如下一行:

stream.Position = 0;

在序列化之后和反序列化之前。

【讨论】:

    【解决方案2】:

    尝试将await cloudBlockBlob.ExistsAsync() 转换为await cloudBlockBlob.ExistsAsync().ConfigureAwait(false)await cloudBlob.OpenReadAsync() 转换为await cloudBlob.OpenReadAsync().ConfigureAwait(false)await _requestFileHandler.HandleFile(stream, name, prefix, cloudBlob.Uri) 转换为await _requestFileHandler.HandleFile(stream, name, prefix, cloudBlob.Uri).ConfigureAwait(false)await JsonSerializer.DeserializeAsync&lt;RequestModel&gt;(stream) 转换为await JsonSerializer.DeserializeAsync&lt;RequestModel&gt;(stream).ConfigureAwait(false)

    当应用await 关键字时,它会暂停调用方法并将控制权交还给调用者,直到等待的任务完成。我们可以避免强制延续/回调回到原始上下文,我们使用ConfigureAwait(false) 来做到这一点。不使用ConfigureAwait(false) 会导致性能问题和不可靠性。由于您没有使用它,stream 有时在调用者上下文中,有时在当前上下文中。当前上下文中的一个为您提供正确的结果。您可以访问 devblogs.microsoft.com/dotnet/configureawait-faq 了解更多信息。

    【讨论】:

      【解决方案3】:

      您可以为您的 EventGridTrigger 函数使用 blob 输入绑定来获取 blob 流,请参见以下代码 sn-p:

      public static async Task Run(JObject eventGridEvent, Stream body, ILogger log)
      {
          log.LogInformation($"{eventGridEvent}");    
          // ...
          var model = await System.Text.Json.JsonSerializer.DeserializeAsync<RequestModel>(body);                  
          // ...
          await Task.CompletedTask;
      }
      

      和 function.json 文件:

       {
        "bindings": [
          {
            "type": "eventGridTrigger",
            "name": "eventGridEvent",
            "direction": "in"
          },
          {
            "type": "blob",
            "name": "body",
            "path": "{data.url}",
            "connection": "rk2019dlstg_STORAGE",
            "direction": "in"
          }
        ],
        "disabled": false
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2013-02-08
        • 1970-01-01
        • 2020-03-21
        • 2015-09-02
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多