Returning Binary Data from ASP .Net Core MVC using Serverless and AWS API Gateway

1.6k Views Asked by At

I have a controller in ASP .Net MVC (dotnet core 2.1) and I'm trying to return a zip file to the user.

[HttpGet]
public async Task<ActionResult> GetLogFiles([FromQuery] string date, 
                                            [FromQuery] string startTime, 
                                            [FromQuery] string endTime)
{
    var decryptedFiles = new Dictionary<string, string>();

    // Code to get log files, decrypt them and add to dictionary

    using (var memoryStream = new MemoryStream())
    {
        using (var archive = new ZipArchive(memoryStream, ZipArchiveMode.Create, true))
        {
            foreach (var (name, data) in decryptedFiles)
            {
                _logger.LogInformation($"Adding {name}");

                var entry = archive.CreateEntry(name);

                using (var entryStream = entry.Open())
                {
                    using (var writer = new StreamWriter(entryStream, Encoding.GetEncoding("Windows-1252")))
                    {
                        writer.Write(data);
                    }
                }
            }
        }

        memoryStream.Seek(0, SeekOrigin.Begin);

        var rawData = memoryStream.ReadAll();

        var response = File(rawData, "application/zip", "logs.zip");

        return response;
    }
}

public static byte[] ReadAll(this Stream stream)
{
    var count = (int) stream.Length;

    var buffer = new byte[count];

    var offset = 0;

    while (offset < count)
    {
        var read = stream.Read(buffer, offset, count - offset);

        if (read == 0)
        {
            throw new EndOfStreamException();
        }

        offset += read;
    }

    return buffer;
}

When I visit the URI in Chrome, it seemingly downloads the zip file however, when I try to open it, Windows claims the file is invalid.

If I make the request in Fiddler, I can see that the server returns a blob of base 64. If I manually decode this base 64 and save the resulting bytes, it results in a valid zip file.

Here are all of the response headers (some redacted for reasons).

HTTP/1.1 200 OK
Content-Type: application/octet-stream
Content-Length: 508
Connection: keep-alive
Date: Tue, 17 Sep 2019 14:04:43 GMT
x-amzn-RequestId: [REDACTED]
Content-Disposition: attachment; filename=logs.zip; filename*=UTF-8''logs.zip
Strict-Transport-Security: max-age=2592000
x-amzn-Remapped-Content-Length: 381
x-amz-apigw-id: [REDACTED]
X-Amzn-Trace-Id: [REDACTED]
X-Cache: Miss from cloudfront
Via: 1.1 [REDACTED]
X-Amz-Cf-Pop: [REDACTED]
X-Amz-Cf-Id: [REDACTED]

Can anyone suggest what I might be doing wrong?

Any advice much appreciated.

Thanks,

Steve.

1

There are 1 best solutions below

0
Stevo On

So, thanks to user mjwills posting this link, it seems it's API Gateway's handling of binary media types.

The next hurdle was the fact that I'm using Serverless so couldn't set this in the CloudFormation.

The way it's configured in serverless.yml is as follows:

provider:
  apiGateway:
    binaryMediaTypes:
      - "application/zip"

Still not quite there. Final piece of the puzzle was to add a header to the request.

Accept: application/zip