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 still occurs:
In a MVC Controller I have a method which provides a file download:
public ActionResult DownloadBigFile()
{
string file = @"C:\Temp\File.txt";
var readStream = new FileStream(file, FileMode.Open, FileAccess.Read);
return File(readStream, "text/plain", "FILE");
}
When the file is below 1 GB the download works fine, above 1 GB an exception is thrown: "Overflow or underflow in the arithmetic operation" with the following details:
Overflow or underflow in the arithmetic operation.
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
Exception Details: System.ArithmeticException: Overflow or underflow in the arithmetic operation.
Source Error:
An unhandled exception was generated during the execution of the current web request. Information regarding the origin and location of the exception can be identified using the exception stack trace below.
Stack Trace:
[ArithmeticException: Overflow or underflow in the arithmetic operation.]
[HttpException (0x80004005): An error occurred while communicating with the remote host. The error code is 0x80070216.]
System.Web.Hosting.IIS7WorkerRequest.RaiseCommunicationError(Int32 result, Boolean throwOnDisconnect) +4081269
System.Web.Hosting.IIS7WorkerRequest.FlushCore(Boolean keepConnected, Int32 numBodyFragments, IntPtr[] bodyFragments, Int32[] bodyFragmentLengths, Int32[] bodyFragmentTypes) +12233777
System.Web.Hosting.IIS7WorkerRequest.FlushCachedResponse(Boolean isFinal) +847
System.Web.HttpResponse.UpdateNativeResponse(Boolean sendHeaders) +1110
System.Web.HttpRuntime.FinishRequestNotification(IIS7WorkerRequest wr, HttpContext context, RequestNotificationStatus& status) +336
Version Information: Microsoft .NET Framework Version:4.0.30319; ASP.NET Version:4.0.30319.34009
The problem is reproducible but I did not find any information about this behavior. How can I prevent this kind of problem or how can I manage big downloads (> 1GB)?
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?
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();
User contributions licensed under CC BY-SA 3.0