Подтвердить что ты не робот

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

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

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

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

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

4b9b3361

Ответ 1

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

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

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

enter image description here

Следующий метод - это то, что я сделал для личного проекта, и этот script хорошо подходит для загрузки и обработки тысяч изображений с высоким разрешением на моем сервере, которые затем были уменьшены до нескольких версий и загружены на 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/

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

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

Ответ 2

Я помещу это здесь как ссылку для любого поиска - это вообще не зависит от 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();
}

?>

Ответ 3

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

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

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

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


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

Ответ 4

Это старый вопрос, но у меня была аналогичная потребность. Я хотел запустить script с помощью команды 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;
}
?>

Ответ 5

Решения:

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

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

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

Ответ 6

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

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

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

Ответ 7

Здесь я просто хотел добавить 2 проблемы в дополнение к тому, что @Jarod Law написал выше fooobar.com/questions/163789/...

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

  • вместо использования 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.
        });
    }
    

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

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

Ответ 8

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

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

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