3

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.

8
  • 1
    @Stevo Possibly a silly question as I'm sure you've checked this, but i'd like to verify: does that memoryStream contain the base64 encoded content? Commented Sep 17, 2019 at 13:41
  • I've tried both application/zip and application/octet-stream. Commented Sep 17, 2019 at 13:45
  • 1
    Ah, the stream you pass into that method will be disposed by the framework, so just remove it from the using. Wrap it in a try/finally if desired. Commented Sep 17, 2019 at 13:54
  • Weird that filename is there twice in the headers. That happens on your local PC? Commented Sep 17, 2019 at 14:12
  • 1
    Right cracked it. @mjwills That link pointed me in the right direction. I'll post the actual details in an answer so it helps anyone else who has this problem. Commented Sep 17, 2019 at 15:51

1 Answer 1

3

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
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.