В нашем веб-приложении (ASP.NET Web Forms) есть страница, на которой будет отображаться недавно созданный PDF файл для пользователей. Поскольку файл PDF иногда довольно велик, мы реализовали "потоковый" подход, чтобы отправить его в клиентский браузер в кусках.
Несмотря на то, что мы отправляем данные в куски, мы знаем полный размер файла перед его отправкой, поэтому мы правильно установили заголовок Content-Length. Это работает в нашей производственной среде некоторое время (и продолжает работать в нашей тестовой среде с практически идентичной конфигурацией) до сегодняшнего дня. Сообщалось о том, что Chrome попытается открыть PDF файл, но будет зависать с анимацией "Загрузка".
Поскольку все еще работало нормально в нашей тестовой среде, я смог использовать Firebug, чтобы взглянуть на заголовки ответов, которые возвращались в обеих средах. В тестовой среде я видел правильный заголовок Content-Length, в то время как в производстве, который был заменен заголовком Transfer-Encoding: chunked. Chrome не нравится это, отсюда зависание.
Я прочитал несколько статей и сообщений о том, как заголовок Transfer-Encoding может отображаться, когда заголовок Content-Length не предоставлен, но мы указываем заголовок Content-Length, и все по-прежнему работает во время работы с тем же код для того же файла PDF на тестовом сервере.
Оба тестовых и производственных сервера работают с IIS 7.5, и оба имеют динамическое и статическое сжатие.
Вот код, о котором идет речь:
var fileInfo = new FileInfo(fileToSendDown);
Response.ClearHeaders();
Response.ContentType = "application/pdf";
Response.AddHeader("Content-Disposition", "filename=test.pdf");
Response.AddHeader("Content-Length", fileInfo.Length.ToString());
var buffer = new byte[1024];
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.Read))
{
int read;
while ((read = fs.Read(buffer, 0, 1024)) > 0)
{
if (!response.IsClientConnected) break;
Response.OutputStream.Write(buffer, 0, read);
Response.Flush();
}
}
Мне посчастливилось увидеть такое же поведение на моей локальной рабочей станции, поэтому, используя отладчик, я смог увидеть, что заголовок "Transfer-Encoding: chunked" устанавливается на втором проходе через цикл while во время вызова 'Промывать'. В этот момент в ответе есть заголовок Content-Length и заголовок Transfer-Encoding, но каким-то образом к моменту достижения ответа в браузере Firebug показывает только заголовок Transfer-Encoding.
UPDATE
Я думаю, что я проследил это, используя комбинацию отправки данных вниз в "кусках" и прикрепляя "Фильтр" к объекту HttpResponse (мы использовали фильтр для отслеживания размера отображаемого состояния просмотра до каждая страница). В нас нет смысла использовать фильтр HTTP при отправке PDF файла в браузер, поэтому очистка фильтра здесь разрешила нашу проблему. Я решил копать немного глубже из любопытства и обновил этот вопрос, если кто-нибудь еще не наткнется на эту проблему в будущем.
У меня есть приложение на AppHarbor, которое воспроизводит проблему: http://transferencodingtest.apphb.com/. Если вы установите флажок "Использовать фильтр"? и "Отправить в куски"? вы должны увидеть заголовок "перекодирование: chunked" (используя инструменты Chrome dev, Firebug, Fiddler, что угодно). Если ни один из полей не установлен, вы получите правильный заголовок длины содержимого. Основной код находится на github, чтобы вы могли видеть, что происходит за кулисами:
https://github.com/appakz/TransferEncodingTest
Обратите внимание, что для локального воспроизведения вам необходимо настроить локальный веб-сайт в IIS 7.5 (7 может также работать, я не пробовал). Сервер разработки ASP.NET, поставляемый с Visual Studio, НЕ воспроизводит проблему.
Я добавил некоторые подробности в сообщение в блоге: Заголовок Content-Length заменен на "Передача-кодирование: Chunked" в ASP. NET