In the Azure functions isolated process model, how can one return a stream without buffering all content first?

Paul D'hertoghe 20 Reputation points
2023-11-07T10:21:12.2866667+00:00

I am migrating Azure functions code from the in-process model to the isolated process model. One function returns a Azure blob file as follows in the in-process model:

        [FunctionName("Download")]
        public async Task<IActionResult> Download(
            [HttpTrigger(AuthorizationLevel.Function, "get", Route = "v1/Download")]
            DownloadRequest downloadRequest,
            HttpRequest req,
            ILogger log)
        {
            // support body content or query parameters for the request
            log.LogInformation("File download request for file {fileId}", downloadRequest.FileId);

            string fileId= req.Query["fileId"];
            if (string.IsNullOrEmpty(fileId))
            {
                return new BadRequestObjectResult("FileId not passed in json body or as query parameter");
            }

            var blobClient = _containerClient.GetBlobClient(fileId);
            if (blobClient == null || !blobClient.Exists())
            {
                return new NotFoundObjectResult($"File {fileId} not found");
            }

            var blobStream = await blobClient.OpenReadAsync().ConfigureAwait(false);

            return new FileStreamResult(blobStream, "application/octet-stream") { FileDownloadName = blobClient.Name };
        }

I have the following code in the isolated process model, but this needs to allocate a byte[], is there any way to stream back the result? I have read that the AspNet.Core integration could take care of that (the function then uses again HttpRequest and IActionResult), but I'm using .NET framework assemblies so I'm afraid that's not possible.

        [Function("Download")]
        public async Task<HttpResponseData> Download(
            [HttpTrigger(AuthorizationLevel.Function, "get", Route = "v1/Download")]
            DownloadRequest downloadRequest,
            HttpRequestData req)
        {
            // support body content or query parameters for the request
            _logger.LogInformation("File download request for file {fileId}", downloadRequest.FileId);

            string fileId= req.Query["fileId"];
            if (string.IsNullOrWhiteSpace(fileId))
            {
                // prefer the query parameters over the json content
                fileId = downloadRequest.FileId;
                securityCode = downloadRequest.SecurityCode;
            }

            var blobClient = _containerClient.GetBlobClient(fileId);
            if (blobClient == null || !blobClient.Exists())
            {
                var response = req.CreateResponse(HttpStatusCode.NotFound);
                response.WriteString($"File {fileId} not found");
                return response;
            }

            var blobStream = await blobClient.OpenReadAsync().ConfigureAwait(false);

            {
                var response = req.CreateResponse(HttpStatusCode.OK);
                response.Headers.Add("content-type", "application/octet-stream");
                response.Headers.Add("content-disposition", $"attachment; filename=\"{blobClient.Name}\"");

                byte[] data = new byte[blobProperties.Value.ContentLength];
                await blobStream.ReadAsync(data, 0, (int)blobProperties.Value.ContentLength);
                await response.WriteBytesAsync(data);
                return response;
            }
        }

Azure Functions
Azure Functions
An Azure service that provides an event-driven serverless compute platform.
5,911 questions
Developer technologies C#
{count} votes

1 answer

Sort by: Most helpful
  1. Ben Gimblett 4,560 Reputation points Microsoft Employee
    2023-11-10T11:33:07.48+00:00

    Hi - The underlying issue you highlight was a gap in functionality for isolated worker - because the worker did not have access to the underlying request/response streams. The product team created an epic to track the work to remediate this (which I believe is now in preview) REF
    https://github.com/Azure/azure-functions-dotnet-worker/issues/1387

    However AFAIK using IActionResult does require aspnet core https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.mvc.iactionresult?view=aspnetcore-7.0

    0 comments No comments

Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.