I have a web api GET method that returns a zip file for downloading. Here's the code that creates the zip archive:
var resultStream = new MemoryStream();
using (var zipArchive = new ZipArchive(resultStream, ZipArchiveMode.Create, leaveOpen: true))
{
foreach (var file in files)
{
zipArchive.CreateEntryFromFile(file.Path, file.Name, CompressionLevel.Optimal);
}
}
And here's how the response gets populated:
var response = new HttpResponseMessage(HttpStatusCode.OK);
response.Content = new ByteArrayContent(resultStream.ToArray());
response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/zip");
response.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment");
response.Content.Headers.ContentDisposition.FileName = "export_" + DateTime.Now.ToString("dd-MM-yyyy_HH-mm-ss") + ".zip";
response.Content.Headers.ContentDisposition.CreationDate = DateTime.Now;
response.Content.Headers.ContentDisposition.Size = resultStream.Length;
response.Content.Headers.ContentLength = resultStream.Length;
The code above works just fine, the problem is it consumes a lot of memory on the server, depending of course on the file sizes. I've tried changing the result to StreamContent
, however this didn't work as the response only returned headers and eventually timed out.
So here are my questions:
- Is there a way to avoid loading all files in memory and instead send the zip file as it gets created?
- Is using StreamContent better to use in this scenario and if yes, what do I need to change to make it work?
- How is buffering affecting memory consumption in each case? I've tried disabling buffering by implementing a custom
IHostBufferPolicySelector
as suggested in this article, but it doesn't appear to have any effect. - The api action currently can be called by navigating a link, using HttpClient or by AJAX request, so any solution has to support all scenarios.
Adapted from the
Kudu
project, a method that usesPushStreamContent
in combination with a specificDelegatingStream
wrapper to stream a zip archive:Which for your case you could use like: