PHP exec - эхо-вывод построчно во время выполнения

Я пытаюсь найти способ, с помощью которого я могу вывести вывод вызова exec, а затем вывести его на экран во время выполнения процесса. Я написал простой PHP-скрипт, который принимает загрузку файла, а затем преобразует файл, если он не соответствует типу файла, используя FFMPEG. Я делаю это на машине с Windows. В настоящее время моя команда выглядит так:

$cmd = "ffmpeg.exe -i ..\..\uploads\\".$filename." ..\..\uploads\\".$filename.".m4v 2>&1";
exec( $cmd, $output);

Мне нужно что-то вроде этого:

while( $output ) {
    print_r( $output);
    ob_flush();  flush();  
}  

Я читал об использовании ob_flush() и flush() для очистки буфера вывода, но я получаю вывод только после завершения процесса. Команда работает отлично, просто не обновляет страницу при конвертации. Я хотел бы получить некоторый результат, чтобы человек знал, что происходит.

Я установил тайм-аут

set_time_limit( 10 * 60 ); //5 minute time out

и было бы очень здорово, если бы кто-то мог направить меня в правильном направлении. Я рассмотрел ряд решений, которые близки к одному Stackoverflow, но, похоже, ни одно из них не сработало.


person Bren1818    schedule 31.03.2014    source источник


Ответы (3)


Поскольку вызов exec является блокирующим вызовом, у вас нет возможности использовать буферы для получения статуса.

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

person Muran    schedule 16.12.2014

exec() блокирует вызов и НЕ возвращает управление PHP, пока внешняя программа не завершится. Это означает, что вы ничего не можете сделать для построчного вывода вывода, потому что PHP приостанавливается, пока работает внешнее приложение.

Для того, что вы хотите, вам нужно использовать proc_open, который возвращает дескриптор файла, из которого вы можете читать в цикле. например

$fh = proc_open('.....');
while($line = fgets($fh)) {
    print($line);
    flush();
}
person Marc B    schedule 31.03.2014

При таком подходе есть две проблемы:

Во-первых, как отмечает @Marc B, тот факт, что exec будет блокироваться до тех пор, пока он не будет завершен. Вам придется придумать способ измерения прогресса.

Во-вторых, использование ob_flush() таким образом равносильно поддержанию соединения между сервером и клиентом открытым и передаче данных понемногу за раз. Это не то, для чего был разработан протокол HTTP, и хотя иногда он может работать, он не будет работать постоянно — разные браузеры и разные серверы будут работать по-разному. Лучший способ сделать это - через вызовы AJAX: используя функцию Javascript setTimeout() (или setInterval()), периодически звоните серверу и сервер отправляет отчет о ходе выполнения.

person Kryten    schedule 31.03.2014