3

I am trying to write an Azure Function v2 using dotnet core as part of a durable function. I want to create an activity function that reads a file from blob storage, decrypts it and returns the decrypted byte stream. I can create the decrypted stream ok and am trying to return the stream like this:

[FunctionName("Decrypt")]
public static async Task<IActionResult> Run(
    [ActivityTrigger] string blobName,
    ILogger log,
    ExecutionContext context)
{
    // get stream from blob storage

    var bytes = await GetDecryptedByteArrayAsync(blobStream);
    return new FileContentResult(bytes, "application/octet-stream");
}

This appears to work, but when I try to read the response like this:

        var provisioningArchive = await
            ctx.CallActivityAsync<FileContentResult>("Decrypt", encryptedBlobName);

I get the following exception:

Newtonsoft.Json.JsonSerializationException: Unable to find a constructor to use for type Microsoft.AspNetCore.Mvc.FileContentResult. A class should either have a default constructor, one constructor with arguments or a constructor marked with the JsonConstructor

How can I get this to deserialise into an object that represents the stream?

Aidan
  • 4,783
  • 5
  • 34
  • 58

2 Answers2

7

I don't think it's possible to return a Stream to an Orchestration context, as the Bindings documentation says that the return values must be serializable to JSON:

Return values - Return values are serialized to JSON and persisted to the orchestration history table in Azure Table storage. These return values can be queried by the orchestration client binding, described later.

I got round this by creating a Dto to wrap the raw byte array:

public class StreamDto
{
    public string Name { get; set; }
    public byte[] Contents { get; set; }
}

and return that instead. Since it is serializable to Json, I can pass it back and forth and recreate the stream in the activity function.

Aidan
  • 4,783
  • 5
  • 34
  • 58
2

Should you be returning a new FileStreamResult?

https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.mvc.filestreamresult.-ctor?view=aspnetcore-2.2

If you use JSON.NET it has this ability... https://www.newtonsoft.com/json/help/html/DeserializeWithJsonSerializerFromFile.htm

Chris Hawkes
  • 11,923
  • 6
  • 58
  • 68
  • 1
    Thanks, I had tried that, but I got a different exception: Microsoft.Azure.WebJobs.Host: Error while handling parameter $return after function returned:. Newtonsoft.Json: Error getting value from 'ReadTimeout' on 'System.IO.MemoryStream'. System.Private.CoreLib: Timeouts are not supported on this stream. – Aidan Jan 15 '19 at 17:16
  • 1
    ugh.. yeah your going to have to write a custom converter to accomplish turning that byte stream to an object. https://stackoverflow.com/questions/30331027/newtonsoft-json-net-how-to-serialize-content-of-a-stream – Chris Hawkes Jan 15 '19 at 17:22
  • 1
    maybe I would be better writing the streams to a storage queue and reading from the next activity function, which is a pity as it would be nice to pass the streams through the orchestration context directly. – Aidan Jan 15 '19 at 17:30