Запретить буферизацию вывода с помощью PHP и Apache
У меня есть PHP-скрипт, который отправляет большое количество записей, и я хочу сбросить каждую запись, как только она станет доступной: клиент может обрабатывать каждую запись по мере ее поступления, ему не нужно ждать полного ответа. Я понимаю, что это займет немного больше времени для всей передачи, потому что он должен быть отправлен в нескольких пакетах, но все же позволяет клиенту начать работать раньше.
Я пробовал все разные flush()
а также ob_flush()
функции, но, кажется, ничто не помогает получить данные, фактически отправленные по линии до завершения страницы. Я подтвердил, что это не веб-браузер, потому что я тестировал его с помощью telnet.
3 ответа
Единственное решение, которое сработало для меня, это установить output_buffering
директива в php.ini "Выкл." Я не хотел делать это для всего сервера, только для этого конкретного ресурса. Обычно вы можете использовать ini_set
из сценария PHP, но по какой-либо причине php не позволяет output_buffering
быть установленным таким образом (см. руководство php).
Что ж, получается, что если вы используете Apache, вы можете установить некоторые директивы php ini (включая output_buffering
) из конфигурации вашего сервера, включая файл.htaccess. Поэтому я использовал следующее в файле.htaccess для отключения output_buffering только для этого одного файла:
<Files "q.php">
php_value output_buffering Off
</Files>
И тогда в моей статической конфигурации сервера мне просто нужно AllowOverride Options=php_value
(или больший молот, как AllowOverride All
) для того, чтобы это было разрешено в файле.htaccess.
Вы не упоминаете, какой веб-сервер вы используете, но я собираюсь выйти на конечность и предположить Apache2. Я ударил почти то же самое, что вы описываете. Я пытался заставить свой скрипт cgi передавать информацию, как она была готова, вместо того, чтобы буферизовать все это. Работал в кайф и т.д., но буферизовался в браузере (почти в любом браузере), что, по крайней мере, сводило с ума. Я прошел точные шаги, которые вы описываете. Решение в моем случае было изменить sites-enabled/terrifico.com
файл конфигурации в Apache2 (рассматриваемая строка начинается с
SetEnvIfNoCase
(Вы можете игнорировать материал выше и ниже этой строки, я просто показываю это для справки о том, где я его разместил.)
<VirtualHost *:80>
ServerAdmin webmaster@localhost
ServerName test.terrifico.com
ServerAlias test.terrifico.com
SetEnvIfNoCase Request_URI \.cgi$ no-gzip dont-vary
DocumentRoot /var/www/test.terrifico.com
Из-за того, что я смотрел на сетевой трафик, идущий взад и вперед, я, наконец, понял, что браузер рекламирует, что он принимает дефляцию для чего-либо (это был текст). В этом была разница между браузером и curl, например. Существенный бит был
Accept-Encoding: GZIP, выкачать, SDCH
Там было немного о chunking
, но это не повлияло на эту конкретную проблему. Итак, браузер запрашивал mod_deflate
пнуть, что побеждало мои тщательно извергаемые байты, как я получил их в моем сценарии cgi. Вы можете изменить его в браузере, но было бы разумнее изменить его на сервере один раз для работы.
Возможно, это помогает.
Отключить буферизацию вывода во время выполнения в PHP без изменения php.ini
или имея .htaccess
файл, просто используйте ob_end_flush()
или же ob_end_clean()
в начале сценария. Например:
Это должно вывести без буферизации:
<?php
ob_end_clean();
for ($i = 0; $i < 5; $i++)
{
echo "$i\n";
flush();
usleep(0.5e6);
}
Это выводит с буферизацией (все за один раз), если output_buffering
включен независимо от flush()
вызов:
<?php
for ($i = 0; $i < 5; $i++)
{
echo "$i\n";
flush();
usleep(0.5e6);
}
Несмотря на свое название, ob_implicit_flush
звонки flush()
не ob_flush()
, неявно после каждого вывода. Это может быть удобно в этом случае после закрытия буфера вывода в начале:
<?php
ob_end_clean(); // disable output buffer
ob_implicit_flush(); // call flush() automatically after every output
for ($i = 0; $i < 5; $i++)
{
echo "$i\n";
usleep(0.5e6);
}
Это исправляет сторону PHP. Там может быть что-то еще происходит с mod_deflate
или похожий (см. ответ Теда Коллинза), и я заметил, что Firefox требуется по крайней мере 1024 байта, прежде чем он вообще начнет что-либо выводить.