如何从 HTTP 触发的 Azure 函数返回 blob?

Posted

技术标签:

【中文标题】如何从 HTTP 触发的 Azure 函数返回 blob?【英文标题】:How do I return a blob from a HTTP triggered Azure Function? 【发布时间】:2019-08-25 05:44:13 【问题描述】:

我正在尝试使用 Azure 函数从 Blob 存储返回文件。就目前而言,我已经让它工作了,但是通过将整个 blob 读入内存,然后将其写回,它的工作效率很低。这适用于小文件,但是一旦它们变得足够大,效率就会非常低。

如何让我的函数直接返回 blob 而不必将其完全读入内存?

这是我目前正在使用的:

public static async Task<HttpResponseMessage> Run(HttpRequestMessage req, Binder binder, TraceWriter log)

    // parse query parameter
    string fileName = req.GetQueryNameValuePairs()
        .FirstOrDefault(q => string.Compare(q.Key, "name", true) == 0)
        .Value;

    string binaryName = $"builds/fileName";

    log.Info($"Fetching binaryName");

    var attributes = new Attribute[]
        
        new BlobAttribute(binaryName, FileAccess.Read),
        new StorageAccountAttribute("vendorbuilds")
    ;

    using (var blobStream = await binder.BindAsync<Stream>(attributes))
    
        if (blobStream == null) 
        
            return req.CreateResponse(HttpStatusCode.NotFound);
        

        using(var memoryStream = new MemoryStream())
        
            blobStream.CopyTo(memoryStream);
            var response = req.CreateResponse(HttpStatusCode.OK);
            response.Content = new ByteArrayContent(memoryStream.ToArray());
            response.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")  FileName = fileName ;
            response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
            return response;
        
    

我的function.json 文件:


  "bindings": [
    
      "authLevel": "anonymous",
      "name": "req",
      "type": "httpTrigger",
      "direction": "in",
      "methods": [
        "get",
        "post"
      ]
    ,
    
      "name": "$return",
      "type": "http",
      "direction": "out"
    
  ],
  "disabled": false

我既不是 C# 开发人员,也不是 Azure 开发人员,所以大部分内容我都忽略了。

【问题讨论】:

您不能只返回文件的链接以及共享访问签名吗? @CSharpRocks 老实说,我什至不知道这是一件事......这可能就是我需要的。如果您将其写为答案,我很乐意接受。谢谢! 【参考方案1】:

我确实喜欢下面的最小内存占用(没有将完整的 blob 保存到内存中的字节中)。请注意,我不是绑定到流,而是绑定到 ICloudBlob 实例(幸运的是,C# 函数支持多种风格的 blob input binding)并返回打开的流。使用内存分析器对其进行了测试,即使对于大型 blob 也没有内存泄漏。

注意:您不需要寻求流位置 0 或刷新或处置(处置将在响应结束时自动完成);

using System.IO;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Microsoft.Azure.Storage.Blob;

namespace TestFunction1

   public static class MyFunction
   
        [FunctionName("MyFunction")]
        public static async Task<IActionResult> Run(
            [HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = "video/fileName")] HttpRequest req,
            [Blob("test/fileName", FileAccess.Read, Connection = "BlobConnection")] ICloudBlob blob,
            ILogger log)
        
            var blobStream = await blob.OpenReadAsync().ConfigureAwait(false);
            return new FileStreamResult(blobStream, "application/octet-stream");
        
   

【讨论】:

为什么要使用ConfigureAwait(false),不使用会不会出问题? @adR , devblogs.microsoft.com/dotnet/configureawait-faq【参考方案2】:

我喜欢 @CSharpRocks 关于创建 SAS 并将链接返回到 blob 存储的建议,但我也发现这篇文章可能相关:

https://anthonychu.ca/post/azure-functions-static-file-server/

以下是相关代码:

var response = new HttpResponseMessage(HttpStatusCode.OK);
response.Content = new StreamContent(memoryStream);
response.Content.Headers.ContentType = 
    new MediaTypeHeaderValue("application/octet-stream");
return response;

【讨论】:

我很确定这是我最初尝试的,但发现它不起作用:github.com/Azure/azure-functions-host/issues/1666【参考方案3】:

感谢@krishg 的回答!

根据您的代码,我想出了相反的方法,将二进制数据从我的 Unity 发布到尽可能干净的 blob 文件。

[FunctionName("Upload")]
        public static async Task<IActionResult> Run(
        [HttpTrigger(AuthorizationLevel.Anonymous, "options", "put", Route = "upload/name")] HttpRequest req,
        [Blob("container/name.bin", FileAccess.Write)] ICloudBlob blob, ILogger log)
        
            if (req.Method == "OPTIONS")
                            
                req.HttpContext.Response.Headers.Add("access-control-allow-methods", "PUT, OPTIONS");
                req.HttpContext.Response.Headers.Add("access-control-allow-headers", "Content-Type");
                return new EmptyResult();
            

            await blob.UploadFromStreamAsync(req.Body).ConfigureAwait(false);            
            return new OkObjectResult("data saved");
        

【讨论】:

以上是关于如何从 HTTP 触发的 Azure 函数返回 blob?的主要内容,如果未能解决你的问题,请参考以下文章

从 Azure Python HTTP 触发函数调用 Azure 逻辑应用

Azure HTTP 触发函数调用返回 417 错误代码

将参数从逻辑应用电子邮件触发器传递到 azure 函数 http 触发器问题

如何从 Azure 函数内部发出基于承诺的 HTTP 发布请求 (NodeJS)?

如何在 azure 函数 http 触发器中触发失败,并带有自定义错误响应

Azure函数HTTP触发器未取消请求