6

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!

Community
  • 1
  • 1
M. Altmann
  • 726
  • 5
  • 20
  • 1
    Did you try adding a `HttpContext.Current.Response.Close()` at the end? Or, if applicable (and you don't need further processing), `HttpContext.Current.ApplicationInstance.CompleteRequest()`? – Jcl Feb 15 '16 at 09:49
  • What is the reason you are not using StreamContent? – martennis Feb 15 '16 at 10:11
  • @Jcl I'll try that and gib you a feedback. – M. Altmann Feb 15 '16 at 12:40
  • @martennis not sure if i've tried that before and had other problems. I'll take it on my list to check – M. Altmann Feb 15 '16 at 12:40
  • 1
    Why are you mixing WebAPI and System.Web (HttpContext.Current)? That's only asking for trouble. – yaakov Feb 19 '16 at 05:21
  • @codran HttpContext.Current seems to be the best option to provide large filedownloads with low system requirements – M. Altmann Feb 22 '16 at 08:09

1 Answers1

1

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.

Tamas
  • 6,260
  • 19
  • 30
  • .Close() didn't solve it. Got an idea how i can prevent sending the HTTP headers? – M. Altmann Feb 22 '16 at 08:06
  • Could you check in Fiddler the headers that are being sent? – Tamas Feb 22 '16 at 10:37
  • Headers only arrive one time. I tend to suppress this exception globally to keep the server event logs clean – M. Altmann Feb 22 '16 at 13:42
  • Do you have any custom filters? Does this issue come up during debugging? You could try to add an exception hanfler filter, and see if that gets executed or not. My guess is that a filter tries to set the response status to some error code, but it can't as it's already sent. – Tamas Feb 22 '16 at 18:34