PHP ob_gzhandler, установка Content-Length отключает сжатый вывод

Я прочитал несколько статей о настройке длины содержимого файла, который будет обслуживаться. Он имеет некоторые преимущества по сравнению с фрагментированными загрузками. Сервер обслуживает фрагменты (в любом случае не знаю, каков размер фрагмента), когда длина содержимого неизвестна.

В PHP я использую ob_gzhandler для сжатия файлов html, js и css и другого текстового содержимого. Установка необработанного content-length перед выводом gzip приводит к странным побочным эффектам, поскольку длина не соответствует длине вывода gzip. Я замечаю задержку или браузер сообщает об ошибке кодирования.

Я видел трюк (также в stackoverflow), чтобы установить длину содержимого после сжатия gzip, чтобы убедиться, что он сообщает правильный размер. Но когда я это делаю, контент больше не сжимается.

Вопрос в том, правильно ли это поведение? Всегда ли сжатое содержимое отправляется фрагментами? Требуется ли длина содержимого только для файлов, которые не сжаты с помощью gzip?

Вот несколько фрагментов кода:

1 Результатов передачи файлов в формате gzip и фрагментов:

ob_start('ob_gzhandler');
echo $sResult;

2 Приводит к нормальной передаче файла с указанной длиной содержимого (но с сохранением gzip и длины содержимого):

ob_start('ob_gzhandler');
echo $sResult;
header('Content-Length: '.ob_get_length()); 

Вот несколько изображений результатов заголовка http: 1 Gzip and Chuncked

2 Ожидается сжатие Gzip, но нормальная передача при установке длины содержимого


person Codebeat    schedule 02.12.2013    source источник
comment
возможный дубликат Как определить длину содержимого gzip-файл?   -  person Ja͢ck    schedule 03.12.2013
comment
Это не дубликат, потому что он / она говорит о GZIP-ФАЙЛЕ. Кроме того, это также может быть файл, но он предназначен для обслуживания контента, а не конкретного файла. Хотите знать, почему он не работает так, как я описал здесь. Не знал, что он полагается на аналогичный метод. До сих пор не знаю / не понимаю, ПОЧЕМУ это работает. Файл CSS, который я использовал в этом примере, не существует, поскольку он состоит из нескольких файлов CSS.   -  person Codebeat    schedule 03.12.2013
comment
Он работает, потому что использует два уровня буферизации вывода; в первом ob_end_flush() он сбрасывает внутреннюю буферизацию вывода, принадлежащую ob_gzhandler; тогда длина содержимого известна ... после этого второй вызов очищает внешний буфер.   -  person Ja͢ck    schedule 03.12.2013
comment
Спасибо за четкое объяснение, Джек!   -  person Codebeat    schedule 03.12.2013
comment
Лично я бы не стал особо беспокоиться о передаче данных по фрагментам; он работает нормально, потому что блоки обычно не читаются один за другим, скорее, все ответы читаются с использованием буферов 8 КБ, а распаковка выполняется в памяти.   -  person Ja͢ck    schedule 03.12.2013
comment
Хорошо, я знаю. Это существует не просто так: см. Ответ: stackoverflow.com/questions/2419281/   -  person Codebeat    schedule 03.12.2013
comment
В этом ответе точно не обсуждаются достоинства такого подхода с точки зрения увеличения производительности. В случае CSS вполне нормально использовать буферизацию вывода, но почему бы просто не сгенерировать заранее CSS и не поручить веб-серверу обрабатывать доставку (используя gzip)? :)   -  person Ja͢ck    schedule 03.12.2013
comment
Я не хочу начинать обсуждение, почему это хорошо или нет. Я думаю, что это нормально. О CSS: потому что он динамический и минимизируется на лету. Исходные файлы находятся на сервере.   -  person Codebeat    schedule 03.12.2013
comment
Даже динамический CSS можно контролировать, хешируя его с чем-то, что изменяется в тандеме, то есть по одному CSS на пользователя. Я просто предполагаю здесь, поэтому без дополнительной информации от вас я просто оставлю это как есть.   -  person Ja͢ck    schedule 03.12.2013
comment
Что вы имеете в виду под хешированием? Как я уже сказал, он будет минимизирован / оптимизирован, что вы не можете сделать с помощью основ.   -  person Codebeat    schedule 03.12.2013
comment
Вы можете сохранить сгенерированный контент один раз, а затем попросить веб-сервер обработать его за вас; возможно, этот ответ может поможет вам понять, как это будет работать, а также объясняет, как кэшировать динамические ресурсы браузера.   -  person Ja͢ck    schedule 03.12.2013


Ответы (1)


Нашел решение / трюк здесь: Как определить длину содержимого сжатого файла?

Сделано в этом:

// clear output buffers
while( ob_get_level() )
 { ob_end_clean(); }

< send http headers here >

ob_start();
ob_start('ob_gzhandler');
echo $sResult;
if( !headers_sent() )
{
   ob_end_flush(); // Flush the output from ob_gzhandler
   header('Content-Length: '.ob_get_length());
   ob_end_flush(); // Flush the outer ob_start()
}

Мне не ясно, почему это работает, но, похоже, работает отлично. Теперь содержимое сжато gzip и имеет длину содержимого (без фрагментов).

Чтобы доказать это, вот скриншот:

gzipped and content-length

Ваше здоровье! ;-)

person Codebeat    schedule 02.12.2013