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

Как доставить большие файлы в ASP.NET Response?

Я не ищу альтернативу содержимого потокового файла из базы данных, действительно, я ищу корень проблемы, это было запуск файла до IIS 6, где мы запускали наше приложение в классическом режиме, теперь мы обновил наш IIS до 7, и мы запускаем пул приложений в режиме конвейера и эта проблема началась.

У меня есть обработчик, где я должен доставлять большие файлы в запрос клиента. И я сталкиваюсь с следующими проблемами:

Файлы имеют средний размер от 4 до 100 МБ, поэтому рассмотрим 80-мегабайтный файл для загрузки файлов.

Буферизация включена, Медленный старт

Response.BufferOutput = True;

Это приводит к очень медленному запуску файла, так как загрузка пользователя и даже индикатор выполнения не отображаются до нескольких секунд, обычно от 3 до 20 секунд, причина в том, что IIS сначала считывает весь файл, определяет длину содержимого, а затем начинает передача файла. Файл воспроизводится в видеоплеере, и он работает очень медленно, однако iPad сначала загружает часть файла, чтобы он работал быстро.

Буферизация выключена, нет длины контента, быстрого запуска, отсутствия прогресса

Reponse.BufferOutput = False;

Это приводит к немедленному запуску, однако конечный клиент (типичный браузер, такой как Chrome) не знает Content-Length, поскольку IIS тоже не знает, поэтому он не отображает прогресс, вместо этого он говорит, что загружен X KB.

Буферизация, Ручная длина содержимого, Быстрый старт, Прогресс и нарушение протокола

Response.BufferOutput = False;
Response.AddHeader("Content-Length", file.Length);

Это приводит к правильной немедленной загрузке файлов в Chrome и т.д., однако в некоторых случаях обработчик IIS приводит к ошибке "Удаленное закрытие клиента" (это очень часто), а другие результаты WebClient приводят к нарушению протокола. Это происходит от 5 до 10% всех запросов, а не от всех запросов.

Я предполагаю, что происходит, IIS не посылает ничего, называемое 100 continue, когда мы не делаем буферизации, и клиент может отключиться, не ожидая выхода. Однако чтение файлов из источника может занять больше времени, но на стороне клиента я увеличил тайм-аут, но, похоже, отключен от IIS и не контролирует.

В любом случае я могу заставить Response отправить 100 continue и не позволить кому-либо закрыть соединение?

UPDATE

Я нашел следующие заголовки в Firefox/Chrome, здесь нет ничего необычного для нарушения протокола или плохого заголовка.

Access-Control-Allow-Headers:*
Access-Control-Allow-Methods:POST, GET, OPTIONS
Access-Control-Allow-Origin:*
Access-Control-Max-Age:1728000
Cache-Control:private
Content-Disposition:attachment; filename="24.jpg"
Content-Length:22355
Content-Type:image/pjpeg
Date:Wed, 07 Mar 2012 13:40:26 GMT
Server:Microsoft-IIS/7.5
X-AspNet-Version:4.0.30319
X-Powered-By:ASP.NET

ОБНОВЛЕНИЕ 2

Turning Recycling все еще не предлагал многого, но я увеличил MaxWorkerProcess до 8, и теперь я получаю меньше ошибок, чем раньше.

Но в среднем из 200 запросов за одну секунду от 2 до 10 запросов терпят неудачу.., и это происходит почти каждые секунды.

ОБНОВЛЕНИЕ 3

Продолжение 5% запросов с ошибкой "Сервер зафиксировал нарушение протокола. Раздел = ResponseStatusLine", у меня есть другая программа, которая загружает контент с веб-сервера, который использует WebClient, и который дает эту ошибку 4-5 раз в секунду, в среднем у меня 5% запросов не удается. Есть ли способ отслеживать сбой WebClient?

Проблемы с переопределением

Полученный файл с нулевым байтом

IIS закрывает соединение по какой-либо причине, на стороне клиента в WebConfig, я получаю 0 байтов для файла, который не равен нулю, мы делаем проверку SHA1 хешей, это говорит нам, что на веб-сервере IIS ошибка не записывается.

Это была моя ошибка, и она была решена, поскольку мы используем Entity Framework, она читала грязные (незафиксированные строки), поскольку чтение не было в области транзакций, поэтому в область транзакции была разрешена эта проблема.

Исключено нарушение правил протокола

WebClient выдает сообщение WebException: "Сервер совершил нарушение протокола. Раздел = ResponseStatusLine.

Я знаю, что могу включить небезопасный синтаксический анализ заголовков, но это не так, когда это мой обработчик HTTP, который отправляет соответствующие заголовки, не знаю, почему IIS отправляет что-нибудь лишнее (проверено на firefox и chrome, ничего необычного), это происходит только 2% раз.

ОБНОВЛЕНИЕ 4

Обнаружена ошибка sc-win32 64, и я где-то читал, что WebLimits для MinBytesPerSecond должен быть изменен с 240 на 0, но все одинаково. Однако я заметил, что всякий раз, когда IIS регистрирует 64 ошибки sc-win32, IIS регистрирует статус HTTP как 200, но была некоторая ошибка. Теперь я не могу включить Failed Trace Logging для 200, потому что это приведет к массивным файлам.

Обе указанные проблемы были решены путем увеличения MinBytesPerSecond, а также отключения сеансов, я добавил подробный ответ, суммирующий каждую точку.

4b9b3361

Ответ 1

Хотя правильный способ доставки больших файлов в IIS - это следующий вариант,

  • Установите MinBytesPerSecond в Zero в WebLimits (это, безусловно, поможет повысить производительность, так как IIS выбирает закрыть клиентов, поддерживающих соединения KeepAlive с меньшими размерами).
  • Выделите больше рабочего процесса в пул приложений, я установил 8, теперь это нужно делать, только если ваш сервер распространяет большие файлы. Это, безусловно, приведет к тому, что другие сайты будут работать медленнее, но это обеспечит лучшие поставки. Мы установили 8, поскольку у этого сервера есть только один веб-сайт, и он просто доставляет огромные файлы.
  • Отключение утилизации пула приложений.
  • Отключить сеансы
  • Оставить буферизацию включен
  • Перед каждым из следующих шагов проверьте, истинно ли Response.IsClientConnected, иначе откажитесь и ничего не отправляйте.
  • Установить Content-Length перед отправкой файла
  • Отметьте ответ
  • Записывать в выходной поток и выполнять регулярные интервалы

Ответ 2

Когда вы задали длину содержимого с bufferOutput равным false, то возможная причина сбоя заключается в том, что IIS пытается gzip отправленный вами файл, и, установив Content-Length IIS, он не сможет изменить его на сжатый, и ошибки начинаются (*).

So keep the BufferOutput to false, and second disable the gzip from iis for the files you send - или отключите iz gzip для всех файлов, и вы программно обрабатываете часть gzip, сохраняя вне gzip файлы, которые вы отправляете.

Некоторые аналогичные вопросы по той же причине: Сайт ASP.NET иногда замерзает и/или показывает нечетный текст в верхней части страницы при загрузке на серверах с балансировкой нагрузки

Сжатие HTTP: некоторые внешние скрипты /CSS не декомпрессируются должным образом в течение некоторого времени

(*) почему бы не изменить его снова? потому что с момента установки заголовка вы не можете принять участие, за исключением случаев, когда вы включили эту опцию в IIS и принимаете случай, когда заголовок не все готов к отправке в браузер.

Последующие действия

Если бы не gziped, следующее, что мне пришло в голову, это то, что файл отправлен и по какой-то причине соединение задерживается, и получил тайм-аут и закрыт. Таким образом, вы получаете "Remote Host Closed The Connection".

Это можно решить в зависимости от причины:

  • Клиент действительно закрыл соединение
  • Тайм-аут выполняется из самой страницы, если вы используете обработчик (опять же, вероятно, сообщение должно быть "Тайм-аут страницы" ).
  • Тайм-аут исходит из ожидания ожидания, страница занимает больше времени выполнения, получает тайм-аут и закрывает соединение. Возможно, в этом случае сообщение было отключено.
  • Пул сделает переработку в момент отправки файла. Отключите все повторные циклы! Это самые возможные случаи, о которых я могу сейчас подумать.

Если он исходит из IIS, перейдите к свойствам веб-сайта и убедитесь, что вы установили самый большой "Тайм-аут подключения" и "Включить HTTP-Keep-Alives".

Тайм-аут страницы, изменив файл web.config(вы можете изменить его программно только для одной конкретной страницы)

<httpRuntime executionTimeout="43200"

Также посмотрите: http://weblogs.asp.net/aghausman/archive/2009/02/20/prevent-request-timeout-in-asp-net.aspx

Замок сеанса

Еще одна вещь, которую вам нужно изучить, - не использовать сеанс на обработчике, который вы используете для отправки файла, потому что сеанс блокирует действие до завершения и если пользователь занимает больше времени для загрузки файла, второй можно получить время.

некоторый относительный:

вызвать страницу aspx, чтобы вернуть изображение в случайном порядке медленнее

Замена сеанса ASP.Net целиком

Функция Response.WriteFile завершается с ошибкой и дает 504 тайм-аут шлюза

Ответ 3

Что бы я сделал, это использовать не столь известный ASP.NET Response.TransmitFile, так как он очень быстро (и, возможно, использует Кеш кеша IIS) и заботится обо всех материалах заголовка. Он основан на неуправляемом Windows TransmitFile API.

Но чтобы иметь возможность использовать этот API, вам нужен физический файл для передачи. Итак, вот псевдо-код С#, который объясняет, как это сделать с вымышленным файловым файлом myCacheFilePath. Он также поддерживает возможности кэширования клиентов. Конечно, если у вас уже есть файл под рукой, вам не нужно создавать этот кеш:

    if (!File.Exists(myCacheFilePath))
    {
        LoadMyCache(...); // saves the file to disk. don't do this if your source is already a physical file (not stored in a db for example).
    }

    // we suppose user-agent (browser) cache is enabled
    // check appropriate If-Modified-Since header
    DateTime ifModifiedSince = DateTime.MaxValue;
    string ifm = context.Request.Headers["If-Modified-Since"];
    if (!string.IsNullOrEmpty(ifm))
    {
        try
        {
            ifModifiedSince = DateTime.Parse(ifm, DateTimeFormatInfo.InvariantInfo);
        }
        catch
        {
            // do nothing
        }

        // file has not changed, just send this information but truncate milliseconds
        if (ifModifiedSince == TruncateMilliseconds(File.GetLastWriteTime(myCacheFilePath)))
        {
            ResponseWriteNotModified(...); // HTTP 304
            return;
        }
    }

    Response.ContentType = contentType; // set your file content type here
    Response.AddHeader("Last-Modified", File.GetLastWriteTimeUtc(myCacheFilePath).ToString("r", DateTimeFormatInfo.InvariantInfo)); // tell the client to cache that file

    // this API uses windows lower levels directly and is not memory/cpu intensive on Windows platform to send one file. It also caches files in the kernel.
    Response.TransmitFile(myCacheFilePath)

Ответ 4

Эта часть кода работает для меня. Он немедленно запускает поток данных клиенту. Он показывает прогресс во время загрузки. Он не нарушает HTTP. Контент-длина заголовка указан и кодировка передачи chuncked не используется.

protected void PrepareResponseStream(string clientFileName, HttpContext context, long sourceStreamLength)
{
    context.Response.ClearHeaders();
    context.Response.Clear();

    context.Response.ContentType = "application/pdf";
    context.Response.AddHeader("Content-Disposition", string.Format("filename=\"{0}\"", clientFileName));

    //set cachebility to private to allow IE to download it via HTTPS. Otherwise it might refuse it
    //see reason for HttpCacheability.Private at http://support.microsoft.com/kb/812935
    context.Response.Cache.SetCacheability(HttpCacheability.Private);
    context.Response.Buffer = false;
    context.Response.BufferOutput = false;
    context.Response.AddHeader("Content-Length", sourceStreamLength.ToString    (System.Globalization.CultureInfo.InvariantCulture));
}

protected void WriteDataToOutputStream(Stream sourceStream, long sourceStreamLength, string clientFileName, HttpContext context)
{
    PrepareResponseStream(clientFileName, context, sourceStreamLength);
    const int BlockSize = 4 * 1024 * 1024;
    byte[] buffer = new byte[BlockSize];
    int bytesRead;
    Stream outStream = m_Context.Response.OutputStream;
    while ((bytesRead = sourceStream.Read(buffer, 0, BlockSize)) > 0)
    {
        outStream.Write(buffer, 0, bytesRead);
    }
    outStream.Flush();
}