I went across some sort of limitation in ASP.NET. I reduced the problem into a sample project in ASP.NET MVC Project (created with Visual Studio 2010 and .NET 4) and the problem
Disable buffering in IIS will do the job:
public ActionResult DownloadBigFile()
{
string file = @"C:\Temp\File.txt";
var readStream = new FileStream(file, FileMode.Open, FileAccess.Read);
Response.BufferOutput = false; //<-----
return File(readStream, "text/plain", "FILE");
}
It really beats me why this is not the default ASP.Net MVC behavior when returning files. Especially when doing it with streams.
References:
Download function failing with big file sizes
How to increase request timeout in IIS?
https://msdn.microsoft.com/en-us/library/system.web.httpserverutility.scripttimeout%28v=vs.110%29.aspx
What worked for me:
If File.Exists(file2return) Then
Dim finfo As New FileInfo(file2return)
Response.Clear()
Response.Buffer = False
Response.BufferOutput = False
Response.ContentType = "application/octet-stream"
Response.AddHeader("Content-Length", finfo.Length.ToString)
Response.AddHeader("Content-Disposition", "attachment; filename=" + finfo.Name)
WriteBytesToResponseBuffered(file2return)
Response.End()
The function that actually writes to the stream output:
Private Sub WriteBytesToResponseBuffered(file2return As String)
Const MAX_BUFFER As Integer = 1024 ^ 2 ' = 1 048 576 bytes = 2^20 = 1 mebibyte = 1 MiB
Dim BytesRead As Integer = 0
Try
Dim Buffer As Byte() = New Byte(MAX_BUFFER - 1) {}
Using myFileStream As New FileStream(file2return, FileMode.Open, FileAccess.Read)
While (InlineAssignHelper(BytesRead, myFileStream.Read(Buffer, 0, MAX_BUFFER))) > 0
Response.OutputStream.Write(Buffer, 0, BytesRead)
End While
End Using
Catch ex As Exception
End Try
End Sub
Private Function InlineAssignHelper(Of T)(ByRef target As T, ByVal value As T) As T
target = value
Return value
End Function
The web.config part: Without this the serving of the page would be terminated if the serving took too much time. This parameter only works when compilation debug="false"
<system.web>
<httpRuntime executionTimeout="600"/>
It is advisable to change this value only for the page serving the file. Instead of changing web.config put this on the page_load:
Page.Server.ScriptTimeout = 60 * 20 ' 20 minutes in this case
If you are saving a file do the OutputStream so you have to disable the buffer at a line before the .Save command, like this:
Response.BufferOutput = false; -- <- Must include this line before the Save method
_zip.Save(Response.OutputStream);
Response.Flush();
Response.End();