Подтвердить что ты не робот

Потоковая загрузка больших файлов в ASP.NET MVC

Для приложения, над которым я работаю, мне нужно разрешить пользователю загружать очень большие файлы - т.е. потенциально много гигабайтов - через наш веб-сайт. К сожалению, ASP.NET MVC, похоже, загружает весь запрос в ОЗУ, прежде чем начать его обслуживать - не совсем идеально подходит для такого приложения. Примечательно, что вы пытаетесь обойти проблему с помощью кода, например:

if (request.Method == "POST")
{
    request.ContentLength = clientRequest.InputStream.Length;
    var rgbBody = new byte[32768];

    using (var requestStream = request.GetRequestStream())
    {
        int cbRead;
        while ((cbRead = clientRequest.InputStream.Read(rgbBody, 0, rgbBody.Length)) > 0)
        {
            fileStream.Write(rgbBody, 0, cbRead);
        }
    }
}

не удается обойти менталитет buffer-the-request-in-RAM. Есть ли простой способ обойти это поведение?

4b9b3361

Ответ 1

Оказывается, мой первоначальный код был в основном правильным; единственное изменение, которое необходимо было изменить:

request.ContentLength = clientRequest.InputStream.Length;

к

request.ContentLength = clientRequest.ContentLength;

Первые потоки во всем запросе для определения длины содержимого; последний просто проверяет заголовок Content-Length, который требует только того, чтобы заголовки были отправлены полностью. Это позволяет IIS начать потоковой запрос почти сразу, что полностью устраняет исходную проблему.

Ответ 2

Конечно, вы можете это сделать. См. Загрузка файлов RESTful с помощью HttpWebRequest и IHttpHandler. Я использую этот метод в течение нескольких лет и имею сайт, который был протестирован с файлами не менее нескольких гигабайт. По сути, вы хотите создать свой собственный IHttpHandler, который проще, чем кажется.

Вкратце, вы создаете класс, который реализует IHttpHandler интерфейс, то есть вы должны поддерживать свойство IsReusable и метод ProcessRequest. Кроме того, есть незначительные изменения в вашем web.config, и это работает как шарм. На этом этапе жизненного цикла запроса весь загружаемый файл не загружается в память, поэтому он аккуратно устраняет проблемы с памятью.

Обратите внимание, что в файле web.config

<httpHandlers>
 <add verb="*" path="DocumentUploadService.upl" validate="false" type="TestUploadService.FileUploadHandler, TestUploadService"/>
</httpHandlers>

файл, на который ссылается, DocumentUploadService.upl, фактически не существует. Это просто чтобы дать альтернативное расширение, чтобы запрос не был перехвачен стандартным обработчиком. Вы указываете форму загрузки файла на этот путь, но затем ваш класс FileUploadHandler запускает и фактически получает файл.

Обновление: На самом деле, код, который я использую, отличается от этой статьи, и я думаю, что наткнулся на причину, по которой он работает. Я использую класс HttpPostedFile, в котором "Файлы загружаются в формате MIME multipart/form-data. По умолчанию все запросы, включая форму поля и загруженные файлы размером более 256 КБ буферизуются на диск, а не хранятся в памяти сервера."

if (context.Request.Files.Count > 0)
{
    string tempFile = context.Request.PhysicalApplicationPath;
    for(int i = 0; i < context.Request.Files.Count; i++)
    {
        HttpPostedFile uploadFile = context.Request.Files[i];
        if (uploadFile.ContentLength > 0)
        {
            uploadFile.SaveAs(string.Format("{0}{1}{2}",
              tempFile,"Upload\\", uploadFile.FileName));
        }
    }
}