Intereting Posts
Найти все слова с strpos () Как преобразовать все символы в их эквивалент сущности html с помощью PHP После того, как поле aliasing sql выбрано, я не могу получить доступ к результату в php Реализация процесса загрузки файлов с помощью Zend Используйте .htaccess, чтобы предотвратить доступ к корневому каталогу из открытого доступа PHP PCNTL – что делает параметр pcntl_signal () restart_syscalls? Обработка загружаемых загрузок plupload на стороне сервера Zend Framework: правильный способ взаимодействия с базой данных? Как получить запрос $ _POST из модуля просмотра? Загрузка фонового изображения Twitter с помощью API и данных с несколькими форматами Что называется сессионным хранилищем? (php oop) статический метод vs нет статического метода Почему Phing запускает мой файл build.xml, а затем говорит, что его не существует? Лучший подход к расширению полей из валидатора PHP-файл Загрузка и загрузка без использования file_get_contents

Показать прогресс для долгого сценария PHP

У меня есть PHP, который, вероятно, займет 10 или (даже много) больше секунд. Я хотел бы показать прогресс для него для пользователя.

В исполняемом классе у меня есть свойство $progress которое обновляется с прогрессом (в 1-100), и метод get_progress() (цель которого должна быть очевидной).

Вопрос в том, как обновить элемент <progress> в передней части для просмотра пользователем?

Я думаю, что AJAX – это решение, но я просто не могу обойти его. (Я не могу добраться до одного экземпляра объекта). Заранее спасибо

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

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

Существует много способов сделать это. Но общий поток процесса выглядит так

введите описание изображения здесь

Следующий метод – это то, что я сделал для личного проекта, и этот скрипт отлично подходит для загрузки и обработки тысяч изображений с высоким разрешением на моем сервере, которые затем были уменьшены до нескольких версий и загружены на amazon s3, при этом распознавая объекты внутри них. (Мой исходный код был в python)

Шаг 1 :

Инициировать транспорт или задачу

Сначала загрузите свой контент, а затем немедленно верните идентификатор транзакции или uuid для этой транзакции через простой запрос POST. Если вы выполняете несколько файлов или несколько вещей в задаче, вы также можете обработать эту логику на этом этапе

Шаг 2:

Выполняйте работу и верните прогресс.

После того, как вы выяснили, как происходят транзакции, вы можете использовать технологию push на стороне сервера для отправки пакета обновления. Я бы выбрал WebSocket или Server Sent Events независимо от того, какое из них применимо, чтобы вернуться к Long Polling в не поддерживаемых браузерах. Простой SSE-метод будет выглядеть так.

 function TrackProgress(upload_id){ var progress = document.getElementById(upload_id); var source = new EventSource('/status/task/' + upload_id ); source.onmessage = function (event) { var data = getData(event); // your custom method to get data, i am just using json here progress.setAttribute('value', data.filesDone ); progress.setAttribute('max', data.filesTotal ); progress.setAttribute('min', 0); }; } request.post("/me/photos",{ files: files }).then(function(data){ return data.upload_id; }).then(TrackProgress); 

На стороне сервера вам нужно будет создать что-то, что будет отслеживать задачи, достаточно простой архитектуры Jobs с job_id, и прогресс, отправленный в db, будет достаточным. Я бы оставил планирование заданий и вам и маршрутизации, но после этого концептуальный код (для простейшего SSE, который будет достаточным для кода выше) выглядит следующим образом.

 <?php header('Content-Type: text/event-stream'); header('Cache-Control: no-cache'); /* Other code to take care of how do you find how many files are left this is really not required */ function sendStatusViaSSE($task_id){ $status = getStatus($task_id); $json_payload = array('filesDone' => $status.files_done, 'filesTotal' => $status.files_total); echo 'data: ' . json_encode( $json_payload ) . '\n\n'; ob_flush(); flush(); // End of the game if( $status.done ){ die(); } } while( True ){ sendStatusViaSSE( $request.$task_id ); sleep(4); } ?> 

Хороший учебник по SSE можно найти здесь http://html5doctor.com/server-sent-events/

и вы можете больше узнать о том, как нажимать обновления с сервера по этому вопросу. Нажатие обновлений с сервера

Вышеупомянутое было концептуальным объяснением, есть другие способы достижения этого, но это было решение, которое повлекло за собой довольно большую задачу в моем случае.

Я помещу это здесь как ссылку для любого, кто ищет – это вообще не зависит от javascript ..

 <?php /** * Quick and easy progress script * The script will slow iterate through an array and display progress as it goes. */ #First progress $array1 = array(2, 4, 56, 3, 3); $current = 0; foreach ($array1 as $element) { $current++; outputProgress($current, count($array1)); } echo "<br>"; #Second progress $array2 = array(2, 4, 66, 54); $current = 0; foreach ($array2 as $element) { $current++; outputProgress($current, count($array2)); } /** * Output span with progress. * * @param $current integer Current progress out of total * @param $total integer Total steps required to complete */ function outputProgress($current, $total) { echo "<span style='position: absolute;z-index:$current;background:#FFF;'>" . round($current / $total * 100) . "% </span>"; myFlush(); sleep(1); } /** * Flush output buffer */ function myFlush() { echo(str_repeat(' ', 256)); if (@ob_get_contents()) { @ob_end_flush(); } flush(); } ?> 

Это очень сложный процесс (FYI) PHP, и ваш запрос AJAX обрабатывается отдельным потоком, поэтому вы не можете получить значение $progress .

Быстрое решение: вы можете записать значение прогресса в $_SESSION['some_progress'] каждый раз, когда оно обновляется, тогда ваш запрос AJAX может получить значение прогресса, $_SESSION['some_progress'] к $_SESSION['some_progress'] .

Вам понадобится setInterval() или setTimeout() для JavaScript, чтобы вызывать обработчик AJAX, пока вы не получите возврат как 100 .

Это не идеальное решение, но его быстро и просто.


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

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

Я сделал это без опроса.

Для второго случая Rikudoit должно быть что-то вроде этого:

JavaScript

 document.getElementById("formatRaid").onclick=function(){ var xhr = new XMLHttpRequest(); xhr.addEventListener("progress", function(evt) { var lines = evt.currentTarget.response.split("\n"); if(lines.length) var progress = lines[lines.length-1]; else var progress = 0; document.getElementById("progress").innerHTML = progress; }, false); xhr.open('POST', "getProgress.php", true); xhr.send(); } 

PHP

 <?php header('Content-Type: application/octet-stream'); header('Cache-Control: no-cache'); // recommended to prevent caching of event data. // Turn off output buffering ini_set('output_buffering', 'off'); // Turn off PHP output compression ini_set('zlib.output_compression', false); // Implicitly flush the buffer(s) ini_set('implicit_flush', true); ob_implicit_flush(true); // Clear, and turn off output buffering while (ob_get_level() > 0) { // Get the curent level $level = ob_get_level(); // End the buffering ob_end_clean(); // If the current level has not changed, abort if (ob_get_level() == $level) break; } while($progress < 100) { // STUFF TO DO... echo '\n' . $progress; } ?> 

Решения:

  1. Опрос Ajax. На стороне сервера хранят прогресс где-то, а затем используют вызов ajax для получения прогресса через равные промежутки времени.

  2. События, отправленные сервером – функция html5, которая позволяет генерировать dom-события с вывода, отправляемого сервером. Это лучшее решение для такого случая, но IE 10 его не поддерживает.

  3. Script / Iframe streaming – используйте iframe для вывода потока из длинного сценария, который будет выводить теги сценария в виде интервалов, которые могут вызвать некоторую реакцию в браузере.

Вы рассматривали вывод javascript и использование потока flush? Это выглядело бы примерно так

 echo '<script type="text/javascript> update_progress('.($progress->get_progress()).');</script>'; flush(); 

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

Здесь я просто хотел добавить 2 проблемы в дополнение к тому, что @Jarod Law написал выше https://stackoverflow.com/a/7049321/2012407

Очень просто и эффективно. Я настраивал и использовал 🙂 Итак, мои 2 проблемы:

  1. вместо использования setInterval() или setTimeout() используют рекурсивный вызов в обратном вызове, например:

     function trackProgress() { $.getJSON(window.location, 'ajaxTrackProgress=1', function(response) { var progress = response.progress; $('#progress').text(progress); if (progress < 100) trackProgress();// You can add a delay here if you want it is not risky then. }); } 

    Так как вызовы асинхронны, они могут вернуться в нежелательном порядке.

  2. сохранение в $_SESSION['some_progress'] является умным, и вы можете, без необходимости хранения базы данных. На самом деле вам нужно разрешить одновременный вызов обоих сценариев и не ставить в очередь PHP. Так что вам больше всего нужно session_write_close() ! Я разместил здесь очень простой демонстрационный пример: https://stackoverflow.com/a/38334673/2012407

Часто в веб-приложениях у нас может быть запрос к задней системе, которая может вызвать длительный процесс, такой как поиск огромного количества данных или длительный процесс работы с базой данных. Затем веб-страница переднего конца может зависать и ждать завершения процесса. Во время этого процесса, если мы сможем предоставить пользователю некоторую информацию о ходе завершения процесса, это может улучшить работу пользователя. К сожалению, в веб-приложениях это кажется непростой задачей, потому что языки веб-скриптов не поддерживают многопоточность, а HTTP – без гражданства. Теперь мы можем использовать AJAX для имитации процесса реального времени.

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

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