I've created a WebAPI Controller - based on MVC 5 - that provides different files for our customers. The tool to access the files is also self written - based on .NET HttpClient - but thats an other story.
In the first version of the download controller I've used the build in mechanism to provide the files like this
But that mechanism crashed on my iis for files > 4GB.
So I finally came to this code:
public class DownloadController : ApiController
{
public async Task Get(long id)
{
string fullFilePath = GetFilePathById(id);
string returnFileName = fullFilePath.Split('\\').Last();
FileInfo path = new FileInfo(fullFilePath);
HttpContext.Current.Response.ContentType = "application/zip";
HttpContext.Current.Response.AddHeader("Content-Length", path.Length.ToString());
HttpContext.Current.Response.AddHeader("Content-Disposition", "attachment; filename=" + returnFileName);
HttpContext.Current.Response.Flush();
try
{
byte[] buffer = new byte[4096];
using (FileStream fs = path.OpenRead())
{
using (BufferedStream bs = new BufferedStream(fs, 524288))
{
int count = 0;
while ((count = bs.Read(buffer, 0, buffer.Length)) > 0)
{
if (!HttpContext.Current.Response.IsClientConnected)
{
break;
}
HttpContext.Current.Response.OutputStream.Write(buffer, 0, count);
HttpContext.Current.Response.Flush();
}
}
}
}
catch (Exception exception)
{
//Exception logging here
}
}
}
That code works very well and I got fast downloads with acceptable CPU usage and disk i/o. But after some time I noticed that - with every single download - an unhandled exception writes an entry into the Application Eventlog of the IIS server like this:
Server cannot set status after HTTP headers have been sent
Exception type: HttpException
Event Log ID 1309
I'm sure that the recurring use of .Flush() causes the problem, but if I remove any of these the download stops working.
In similar questions I can find Response.BufferOutput = true;
as a solution but that seems to eat all my server resources and delays the download.
Any suggestions would be great!
The problem is not with the Flush()
, but that you are not closing the response stream yourself with HttpContext.Current.Response.Close();
The ASP.NET framework doesn't know what you are doing inside the action method, so it passes the request through the usual request pipe, which does all the necessary plumbing instead of us. One of this is that it sends the headers and HTTP status to the client. However you have already set and sent the headers when the framework tries to do that. To avoid this you should close the stream, and finish the processing yourself by closing the response stream.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With