I'm developing a website with ASP.NET MVC 5 + Web API. One of the requirements is that users must be able to download a large zip file, which is created on the fly. Because I immediately want to show progress of the user, my idea was to use a PushStreamContent with a callback in the resonse. The callback creates the zipfile and streams it to the response.
When I implement this as follows, starting from an empty ASP.NET MVC + Web API project, it works as expected. As soon as the result is returned to the client, the callback gets invoked and the zipfile is streamed to the client. So the user can see progress as soon as the callback creates the zip archive and add files to it.
[RoutePrefix("api/download")]
public class DownloadController : ApiController
{
[HttpGet]
public HttpResponseMessage Get()
{
var files = new DirectoryInfo(@"c:\tempinput").GetFiles();
var pushStreamContent = new PushStreamContent(async (outputStream, httpContext, transportContext) =>
{
using (var zipOutputStream = new ZipOutputStream(outputStream))
{
zipOutputStream.CompressionLevel = CompressionLevel.BestCompression;
foreach (var file in files)
{
zipOutputStream.PutNextEntry(file.Name);
using (var stream = File.OpenRead(file.FullName))
{
await stream.CopyToAsync(zipOutputStream);
}
}
}
});
var response = new HttpResponseMessage(HttpStatusCode.OK)
{
Content = pushStreamContent
};
response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
response.Content.Headers.ContentDisposition =
new ContentDispositionHeaderValue("attachment") {FileName = "MyZipfile.zip"};
return response;
}
}
Now, I have to integrate this in an existing website, which is configured to use Microsoft.Owin.OwinMiddleware. I used the same code as pasted above, but now the behavior is different: during the creation of the zipfile, it 's not streamed to the response, but only downloaded when the creation of the zip has finished. So the user doesn't see any progress during the creation of the file.
I also tried a different approach in my Web API + Owin project, as described here: (generate a Zip file from azure blob storage files). In an empty Asp.NET MVC project (without OWIN middleware), this works exactly as expected, but when OWIN is involved, I get this HTTPException and stacktrace:
System.Web.HttpException: 'Server cannot set status after HTTP headers have been sent.'
System.Web.dll!System.Web.HttpResponse.StatusCode.set(int value) Unknown System.Web.dll!System.Web.HttpResponseWrapper.StatusCode.set(int value) Unknown Microsoft.Owin.Host.SystemWeb.dll!Microsoft.Owin.Host.SystemWeb.OwinCallContext.Microsoft.Owin.Host.SystemWeb.CallEnvironment.AspNetDictionary.IPropertySource.SetResponseStatusCode(int value) Unknown
It seems that OWIN wants to set a response status, although that was already done in my Get() method (HttpResponseMessage(HttpStatusCode.OK)).
Any suggestions how to fix this or ideas for a different approach?
Thanks a lot!