Потоковая передача больших загрузок файлов в 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-into-RAM. Есть ли простой способ обойти такое поведение?

2 56

2 ответа:

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

request.ContentLength = clientRequest.InputStream.Length;

до

request.ContentLength = clientRequest.ContentLength;

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

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

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

обратите внимание, что в интернете.config,

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

файл, на который ссылается DocumentUploadService.УПЛ, на самом деле не существует. То есть просто там, чтобы дать альтернативное расширение, чтобы запрос не был перехвачен стандартный обработчик. Вы указываете форму загрузки файла на этот путь, но затем ваш класс 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));
        }
    }
}