3

I have binary data in database or Azure Blob, I want to download the file when user hit the Azure Function. How to do it? Seems that HttpResponseData is the way to go, but how? I could not find any example on internet.

I only found some examples of earlier versions of Azure Function (such as Using Azure Function (.NET Core) to Download a file) If I use that example with IActionResult as return type of Run() function, I got this as response:

 {
  "FileContents": "/9j/4AAQSkZJRgABAQEA......UUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAf/9k=",
  "ContentType": "image/jpeg",
  "FileDownloadName": "f69bea69-49cd-4a64-a035-8a26505c578e.jpg",
  "LastModified": null,
  "EntityTag": null,
  "EnableRangeProcessing": false
}

Can someone please give me a working example?

If it's from Azure Blob Storage, then I can get a Stream, what is the most efficient way to do it? I don't want to load a 1 GB file into memory then download it to browser.

Thank you!

Zoe
  • 27,060
  • 21
  • 118
  • 148

1 Answers1

1

Here is how I implemented downloading of a file in Azure function with .NET 5:

HttpResponseData response = req.CreateResponse(hasRangeHeader ? HttpStatusCode.PartialContent : HttpStatusCode.OK);
response.WriteBytes(memoryStream.ToArray());
response.Headers.Add("Content-Type", properties.Value.ContentType);
response.Headers.Add("Accept-Ranges", $"bytes");
response.Headers.Add("Content-Disposition", $"attachment; filename={blobClient.Name}; filename*=UTF-8'{blobClient.Name}");
return response;

And here is the complete function class:

using Azure.Storage.Blobs;
using Azure.Storage.Blobs.Models;
using Microsoft.Azure.Functions.Worker;
using Microsoft.Azure.Functions.Worker.Http;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Text.RegularExpressions;
using System.Threading.Tasks;

namespace UploadMediaFunction
{
    public class GetMediaFileFunction
    {
        private readonly ILogger _logger;

        public GetMediaFileFunction(ILoggerFactory loggerFactory)
        {
            _logger = loggerFactory.CreateLogger<GetMediaFileFunction>();
        }

        [Function("GetMediaFile")]
        public async Task<HttpResponseData> Run([HttpTrigger(AuthorizationLevel.Anonymous, "get")] HttpRequestData req, string name)
        {
            if (string.IsNullOrEmpty(name))
                throw new ArgumentNullException(nameof(name));

            BlobContainerClient container = await GetCloudContainer();
            BlobClient blobClient = container.GetBlobClient(name);
            BlobDownloadInfo download = await blobClient.DownloadAsync();
            var properties = blobClient.GetProperties();

            long startPosition = 0;
            bool hasRangeHeader = false;
            if (req.Headers.TryGetValues("Range", out IEnumerable<string> values) && values.Any())
            {
                string[] range = values.First().ToString().Split(new char[] { '=', '-' });
                startPosition = long.Parse(range[1]);
                hasRangeHeader = true;
            }

            MemoryStream memoryStream = new();
            download.Content.CopyTo(memoryStream);
            memoryStream.Position = startPosition;

            HttpResponseData response = req.CreateResponse(hasRangeHeader ? HttpStatusCode.PartialContent : HttpStatusCode.OK);
            response.WriteBytes(memoryStream.ToArray());
            response.Headers.Add("Content-Type", properties.Value.ContentType);
            response.Headers.Add("Accept-Ranges", $"bytes");
            response.Headers.Add("Content-Disposition", $"attachment; filename={blobClient.Name}; filename*=UTF-8'{blobClient.Name}");
            return response;
        }

        public static async Task<BlobContainerClient> GetCloudContainer()
        {
            var containerName = Environment.GetEnvironmentVariable("BlobStorageContainerName");
            containerName = Regex.Replace(containerName, @"[^0-9a-zA-Z]+", "-").Replace("--", "-");
            var connectionString = Environment.GetEnvironmentVariable("BlobStorageStorageConnectionString");

            BlobServiceClient blobServiceClient = new(connectionString);

            var blobContainerClient = blobServiceClient.GetBlobContainerClient(containerName);
            if (!blobContainerClient.Exists())
                await blobContainerClient.CreateAsync();

            return blobContainerClient;
        }
    }
}
Vangi
  • 586
  • 6
  • 20
  • I believe that `memoryStream.ToArray()` is precisely what the OP does _not_ want to do. That loads the entire file into memory at once instead of streaming it into the response. – DannyMeister Jul 06 '23 at 15:59