【问题标题】:Web Service to download files from Azure throws exception for large files从 Azure 下载文件的 Web 服务对大文件抛出异常
【发布时间】:2019-06-05 18:09:19
【问题描述】:

我正在开发 Azure 中的 Web 服务框架,以及与 Azure 通信的网站。网站应使用文件名调用 Azure,Azure 中的 Web 服务应在 Blob 存储中找到给定文件并将其发送回客户端进行下载。

只要返回的文件很小,我就可以让它工作(使用 20mb mp4 文件进行测试可以正常工作),但是像 1Gb mp4 文件这样的文件最终会引发异常。

C#请求代码:

public override async Task ProcessRequestAsync(HttpContext context)
{
    JavaScriptSerializer oSearlizer = new JavaScriptSerializer();
    object o = new
    {
        sourceStorageAccountName = "accountName",
        sourceStorageAccountKey = "accountKey",
        sourceContainer = "test"
    };

    string req = oSearlizer.Serialize(o);
    HttpContent content = new StringContent(req, Encoding.UTF8, "application/json");
    HttpClient client = new HttpClient();

    HttpResponseMessage x = await client.PostAsync("http://localhost:7071/api/get_file", content);
    context.Response.Clear();
    context.Response.Buffer = false;
    context.Response.BufferOutput = false;
    context.Response.ContentType = "video/mp4";
    context.Response.AddHeader("content-disposition", "attachment;filename=test.mp4"); // Save file         
    context.Response.Charset = "";
    context.Response.Cache.SetCacheability(HttpCacheability.NoCache);
    context.Response.BinaryWrite(await x.Content.ReadAsByteArrayAsync());
    context.Response.End();
}

Azure 网络服务代码:

CloudBlobContainer sourceBlobContainer = CopyBlobHelpers.GetCloudBlobContainer(_sourceStorageAccountName, _sourceStorageAccountKey, _sourceContainer);
CloudBlockBlob blob = sourceBlobContainer.ListBlobs().First();

HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.OK);
Stream stream = blob.OpenRead(null, new BlobRequestOptions() { ServerTimeout = new System.TimeSpan(2, 59, 59), MaximumExecutionTime = new System.TimeSpan(2, 59, 59) });
response.Content = new StreamContent(stream);
response.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment");
response.Content.Headers.ContentDisposition.FileName = fileNameZip;
response.Content.Headers.ContentType = new MediaTypeHeaderValue("video/mp4");

return response;

就像我说的,此代码适用于 20mb 的 mp4 文件,但对于 1Gb 或更大的 mp4 文件会引发异常。我得到的异常是:“异常:SocketException:现有连接被远程主机强行关闭”

如果我从请求代码中删除“await”并改用“.Result”,我会得到一个不同的异常:“异常:HttpRequestException:将内容复制到流时出错。”

单步执行代码,进入“await client.PostAsync()”,我单步执行Azure Web服务代码,直到返回语句。然后我再一步,1-2分钟没有任何反应,然后抛出异常。

有人对可能发生的事情有任何想法吗?我认为这与试图通过过多数据或某处超时的流有关,但我所做的似乎没有解决问题。

【问题讨论】:

    标签: c# azure azure-functions


    【解决方案1】:

    由于 Azure Functions 存在默认超时时间限制,如下所示,因此您将收到问题 Exception: SocketException: An existing connection was forcibly closed by the remote host 从 Azure Functions 下载大文件。请参考Functions limits

    因此,这些小文件可以在小于超时时长的短时间内下载,但大文件会在超时后被强制关闭。

    解决方案是从 Azure Function 用 SAS 令牌响应 blob url 到客户端,然后直接从 Azure Blob Storage 通过 sas url 下载 blob,因为 sas url 没有额外的身份验证,Azure Storage 没有超时限制。

    如果不熟悉如何为 blob 生成 SAS 令牌,可以参考官方示例代码Getting Started with Shared Access Signatures (SAS)

    【讨论】:

      【解决方案2】:

      我添加然后删除了关于使用HttpClient.Timeout 的评论,因为我认为虽然这可能会解决问题,但您使用的设计存在问题。您正在使用 POST 请求来获取资源。你真的应该使用 GET 请求。通常,它的工作方式是你会调用类似的东西

      GET http://localhost:7071/api/{file_name}

      其中{file_name} 是您要下载的文件的名称。该服务通常会知道有关存储帐户的信息,因此您不需要当前拥有的请求正文。您可能希望该服务不知道从哪个存储帐户下载,但我认为这不太可能。

      我还觉得有必要注意,如果您要继续通过网络发送存储帐户信息,则需要使用 HTTPS 而不是 HTTP,因为就目前而言,您正在发送存储帐户密钥明文。

      【讨论】:

      • 虽然这些都是好点,但它们不一定能解决我遇到的问题。即使添加了 3 小时的 HttpClient.Timeout,连接也会在约 1 分钟后关闭。
      猜你喜欢
      • 2015-09-12
      • 2020-07-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-01-07
      • 2015-04-04
      • 2014-10-04
      相关资源
      最近更新 更多