我正在尝试使用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开发人员,因此大部分内容使我无所适从。
答案 0 :(得分:2)
我在下面做了类似的事情,以最小化内存占用(不将完整的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");
}
}
}
答案 1 :(得分:0)
我喜欢创建SAS并将链接返回到Blob存储的@CSharpRocks建议,但我也发现本文可能相关:
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;
答案 2 :(得分:0)
感谢@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");
}