Как сбросить данные в браузер, но продолжить выполнение

У меня есть ob_start() и соответствующий ob_flush(). Я хочу очистить часть данных и продолжить выполнение остальных. Использование ob_flush() не помогло. Также, если возможно, отдых должен происходить без отображения загрузки в браузере.

РЕДАКТИРОВАТЬ:

Я не хочу использовать ajax


person aWebDeveloper    schedule 14.05.2012    source источник
comment
После того, как вы выполните первый ob_flush, хотите ли вы, чтобы другой вывод отображался? Или вы хотите, чтобы запрос был полностью завершен, насколько может судить клиент, а затем какая-то обработка продолжалась в фоновом режиме?   -  person Corbin    schedule 14.05.2012
comment
Все, что вам нужно, это flush (), если буферизация вывода уже отключена в php.ini, однако для этого вам необходимо настроить Apache, NginX и т. Д.   -  person PJ Brunet    schedule 13.11.2013


Ответы (7)


ob_flush записывает буфер. Другими словами, ob_flush сообщает PHP, чтобы он выдал Apache (или nginx / lighttpd / что-то еще), а затем PHP забыл об этом. Как только Apache получает результат, он делает с ним все, что хочет. (Другими словами, после ob_flush это вне вашего контроля, будет оно немедленно записано в браузер или нет).

Итак, короткий ответ: нет гарантированного способа сделать это.

Просто предположите, вы, вероятно, ищете AJAX. Когда люди пытаются манипулировать загрузкой содержимого страницы, как вы это делаете, AJAX почти всегда является правильным путем.

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

Я бы попытался извлечь его из Интернета. Это может означать запись в cron или просто запуск фонового процесса изнутри PHP (процесс, который, хотя и запущен изнутри выполнения скрипта, не умрет вместе со скриптом, и скрипт не будет ждать его завершения перед смертью).

Если вы действительно пойдете по этому пути, это будет означать, что вы даже можете создать какую-то систему статусов, если это необходимо. Затем вы можете отслеживать выполнение и периодически сообщать пользователю о ходе выполнения. (Технически вы также можете создать систему состояний с помощью сценария ignore_user_abort-ed, но мне он не кажется таким чистым.)

person Corbin    schedule 14.05.2012
comment
когда новый пользователь входит в систему, я хочу сэкономить много данных. Эти данные должны быть немедленно сохранены, но не прерывать работу пользователя, т.е. показывать загрузку в браузере. - person aWebDeveloper; 14.05.2012

Я делал это в прошлом, и вот как я решил это:

ob_start();

/*
 * Generate your output here
 */ 

// Ignore connection-closing by the client/user
ignore_user_abort(true);

// Set your timelimit to a length long enough for your script to run, 
// but not so long it will bog down your server in case multiple versions run 
// or this script get's in an endless loop.
if ( 
     !ini_get('safe_mode') 
     && strpos(ini_get('disable_functions'), 'set_time_limit') === FALSE 
){
    set_time_limit(60);
}

// Get your output and send it to the client
$content = ob_get_contents();         // Get the content of the output buffer
ob_end_clean();                      // Close current output buffer
$len = strlen($content);             // Get the length
header('Connection: close');         // Tell the client to close connection
header("Content-Length: $len");     // Close connection after $len characters
echo $content;                       // Output content
flush();                             // Force php-output-cache to flush to browser.
                                     // See caveats below.

// Optional: kill all other output buffering
while (ob_get_level() > 0) {
    ob_end_clean();
}

Как я уже говорил в нескольких комментариях ранее, вам следует следить за сжатием вашего контента, поскольку это изменит длину вашего контента, но не изменит заголовок об этом. Он также может буферизовать ваш вывод, поэтому он не будет мгновенно отправлен клиенту.
Вы можете попробовать сообщить apache, чтобы он не архивировал ваш контент, используя apache_setenv('no-gzip', '1');. Но это не сработает, если вы используете правила перезаписи для перехода на свою страницу, поскольку тогда он также изменит эти переменные среды. По крайней мере, так было для меня.

См. Дополнительные предупреждения о передаче вашего контента пользователю в руководстве.

person Zombaya    schedule 14.05.2012
comment
Просто к вашему сведению, функция PHP - ob_get_content S - person Bob Gregor; 12.11.2013

это моя функция

function bg_process($fn, $arr) {
    $call = function($fn, $arr){
        header('Connection: close');
        header('Content-length: '.ob_get_length());
        ob_flush();
        flush();
        call_user_func_array($fn, $arr);
        };
    register_shutdown_function($call, $fn, $arr);
    }

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

function test() {
    while (true) {
        echo 'this text will never seen by user';
        }
    }

вот как вызвать функцию

bg_process('test'); 

первый аргумент - callable, второй аргумент - это передаваемый массив для проверки функции с индексированным массивом

Примечание: я не использую ob_start() в начале скрипта.

person Afrig Aminuddin    schedule 17.05.2012

В моем блоге есть статья, объясняющая, как этого можно достичь с помощью apache / mod_php: http://codehackit.blogspot.com/2011/07/how-to-kill-http-connection-and.html Надеюсь, это поможет, ура

person smassey    schedule 14.05.2012
comment
Было бы полезно, если бы вы добавили здесь небольшой фрагмент того, как это работает, поэтому на случай, если ваш сайт когда-нибудь выйдет из строя, у нас все еще есть ответ. Код, который вы написали в своем блоге, работает. Однако, если вы сказали apache раньше, что нужно сжать ваш контент, он больше не будет работать (так как длина больше не будет правильной) - person Zombaya; 14.05.2012

Если вы используете PHP-FPM:

ignore_user_abort(true);
fastcgi_finish_request();

Две указанные выше функции являются ключевыми факторами, которые ignore_user_abort предотвращают ошибку, а fastcgi_finish_request закрывают клиентское соединение.

person Nick Tsai    schedule 28.07.2018

fastcgi_finish_request

Эта функция сбрасывает все данные ответа клиенту и завершает запрос. Это позволяет выполнять трудоемкие задачи, не оставляя соединение с клиентом открытым.

не работает на Apache. (PHP 5 ›= 5.3.3, PHP 7)

person sun    schedule 25.05.2018

Использовать:

header("Content-Length: $len");

..где $len - длина данных, передаваемых клиенту.

У меня нет предыстории, чтобы знать, когда и где это сработает, но я попробовал несколько браузеров, и все сразу вернулось с:

<?PHP 
    header("Content-length:5");
    echo "this is more than 5";
    sleep(5);
?>

изменить: Chrome, IE и Opera показали this, а FireFox - this is more than 5. Однако после этого все они закрыли заявку.

person mowwwalker    schedule 14.05.2012
comment
Вот почему длина содержимого должна быть такой же, как и фактическое содержимое. И остерегайтесь gzip, так как это изменит длину вашего ответа. - person Zombaya; 14.05.2012
comment
@Zombaya, я не понимаю, насколько это актуально. Пока он не пытается отключить свой вывод после определенного количества, это должно работать, чтобы завершить запрос после отправки необходимых данных. - person mowwwalker; 14.05.2012
comment
Что ж, когда я пытался это сделать, и длина не соответствовала фактической длине, соединение не всегда закрывалось и продолжало загружаться. - person Zombaya; 14.05.2012
comment
@Zombaya, почему длина не соответствует реальной длине? Почему он не мог просто сделать header("Content-Length: ".strlen($content)); - person mowwwalker; 14.05.2012
comment
Это работает нормально, но если вы настроили apache для использования сжатия gzip на выводимых веб-страницах, он изменяет содержимое вашего ответа, но не изменяет заголовок content-length-header. В прошлом у меня были проблемы с этим. - person Zombaya; 14.05.2012
comment
@Zombaya, понятно, в этом есть смысл. Я полагаю, что есть какая-то магия http, которая имела бы аналогичный эффект. - person mowwwalker; 14.05.2012