ASP.NET Overflow или underflow в арифметической операции при возврате большого файла больше 1 ГБ

Я столкнулся с каким-то ограничением в ASP.NET. Я уменьшил проблему до примера проекта в ASP.NET MVC Project (создан с помощью Visual Studio 2010 и .NET 4), и проблема по-прежнему возникает:

В контроллере MVC у меня есть метод, который обеспечивает загрузку файла:

public ActionResult DownloadBigFile()
{
    string file = @"C:\Temp\File.txt";
    var readStream = new FileStream(file, FileMode.Open, FileAccess.Read);
    return File(readStream, "text/plain", "FILE");
}

Если размер файла меньше 1 ГБ, загрузка работает нормально, выше 1 ГБ выдается исключение: "Переполнение или недостаточный объем в арифметической операции" со следующими сведениями:

 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 

Проблема воспроизводима, но я не нашел никакой информации об этом поведении. Как я могу предотвратить подобные проблемы или как управлять большими загрузками (> 1 ГБ)?

Ответ 1

Отключить буферизацию в IIS выполнит задание:

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");
}

Это действительно бьет меня, почему это не по умолчанию поведение ASP.Net MVC при возврате файлов. Особенно, когда вы делаете это с потоками.

Ответ 2

Рекомендации:

Ошибка загрузки функции с большими размерами файлов

Как увеличить время ожидания запроса в IIS?

https://msdn.microsoft.com/en-us/library/system.web.httpserverutility.scripttimeout%28v=vs.110%29.aspx

Что сработало для меня:

            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()

Функция, которая фактически записывает в поток вывода:

    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

Часть web.config: без этого обслуживание страницы было бы прекращено, если бы обслуживание занимало слишком много времени. Этот параметр работает только при компиляции debug = "false"

  <system.web>
<httpRuntime executionTimeout="600"/>

Рекомендуется изменять это значение только для страницы, обслуживающей файл. Вместо изменения web.config поместите это в page_load:

Page.Server.ScriptTimeout = 60 * 20 ' 20 minutes in this case

Ответ 3

Если вы сохраняете файл, выполните OutputStream, поэтому вы должны отключить буфер в строке перед командой .Save, например:

            Response.BufferOutput = false; -- <- Must include this line before the Save method
            _zip.Save(Response.OutputStream);
            Response.Flush();
            Response.End();