如何在控制器中调用 Symfony2 控制台命令并进行流式输出?
How to call Symfony2 console command within controller and have streamed output?
所以我有一个基本要求:我需要从控制器调用 Symfony2 的自定义控制台命令(脚本也由 CRON 作业调用,但我希望它可以从网络浏览器调用)。
我跟着 this tutorial 让它工作,这里是:
<?php
namespace AppBundle\Controller\Admin;
use Symfony\Bundle\FrameworkBundle\Console\Application;
use Symfony\Component\Console\Input\ArrayInput;
use Symfony\Component\Console\Output\BufferedOutput;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
class TacheController extends Controller
{
/**
* @Route("/admin/taches/facebook", name="admin_tache_facebook")
*
* @return Response
*/
public function facebookAction(Request $request)
{
ini_set('max_execution_time', -1);
$kernel = $this->get('kernel');
$application = new Application($kernel);
$application->setAutoExit(false);
$input = new ArrayInput(array(
'command' => 'nrv:fetch:facebook',
));
// You can use NullOutput() if you don't need the output
$output = new BufferedOutput();
$application->run($input, $output);
// return the output, don't use if you used NullOutput()
$content = $output->fetch();
// return new Response(""), if you used NullOutput()
return new Response($content);
}
}
但是,控制台命令很长 运行 (~200 万),因此,页面挂起一段时间,直到它显示命令的所有输出。
我的目标是让输出在控制台中显示,就像在 ConsoleBundle
中使用 Web 控制台一样。我想到了 ob_start()
和 ob_end_flush()
的使用,但我不知道如何在这种情况下使用它们。
我想要实现的目标是否可行?我怎样才能做到这一点?
解决方案
根据 ,我必须扩展 \Symfony\Component\Console\Output\BufferedOutput
并实现 doWrite()
方法。这是:
<?php
namespace AppBundle\Console;
use Symfony\Component\Console\Output\BufferedOutput;
use Symfony\Component\HttpFoundation\StreamedResponse;
class StreamedOutput extends BufferedOutput
{
public function doWrite($message, $newline)
{
$response = new StreamedResponse();
$response->setCallback(function() use($message) {
echo $message;
flush();
});
$response->send();
}
}
并对控制器进行如下修改:
$output = new StreamedOutput();
结果是页面在命令执行后立即流式传输命令的输出(而不是等待它完成)。
您将必须实现自己的输出 class 并注入它而不是 BufferedOutput。您可以扩展抽象 class \Symfony\Component\Console\Output\Output
或实现接口 vendor/symfony/symfony/src/Symfony/Component/Console/Output/OutputInterface.php:20
.
您的实施应该像此处描述的那样流式传输响应:http://symfony.com/doc/current/components/http_foundation/introduction.html#streaming-a-response 而不是等待命令执行结束并将所有输出一起发回。当然,只有当您的命令在执行过程中打印出某些内容时,这才会起作用。
所以我有一个基本要求:我需要从控制器调用 Symfony2 的自定义控制台命令(脚本也由 CRON 作业调用,但我希望它可以从网络浏览器调用)。
我跟着 this tutorial 让它工作,这里是:
<?php
namespace AppBundle\Controller\Admin;
use Symfony\Bundle\FrameworkBundle\Console\Application;
use Symfony\Component\Console\Input\ArrayInput;
use Symfony\Component\Console\Output\BufferedOutput;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
class TacheController extends Controller
{
/**
* @Route("/admin/taches/facebook", name="admin_tache_facebook")
*
* @return Response
*/
public function facebookAction(Request $request)
{
ini_set('max_execution_time', -1);
$kernel = $this->get('kernel');
$application = new Application($kernel);
$application->setAutoExit(false);
$input = new ArrayInput(array(
'command' => 'nrv:fetch:facebook',
));
// You can use NullOutput() if you don't need the output
$output = new BufferedOutput();
$application->run($input, $output);
// return the output, don't use if you used NullOutput()
$content = $output->fetch();
// return new Response(""), if you used NullOutput()
return new Response($content);
}
}
但是,控制台命令很长 运行 (~200 万),因此,页面挂起一段时间,直到它显示命令的所有输出。
我的目标是让输出在控制台中显示,就像在 ConsoleBundle
中使用 Web 控制台一样。我想到了 ob_start()
和 ob_end_flush()
的使用,但我不知道如何在这种情况下使用它们。
我想要实现的目标是否可行?我怎样才能做到这一点?
解决方案
根据 \Symfony\Component\Console\Output\BufferedOutput
并实现 doWrite()
方法。这是:
<?php
namespace AppBundle\Console;
use Symfony\Component\Console\Output\BufferedOutput;
use Symfony\Component\HttpFoundation\StreamedResponse;
class StreamedOutput extends BufferedOutput
{
public function doWrite($message, $newline)
{
$response = new StreamedResponse();
$response->setCallback(function() use($message) {
echo $message;
flush();
});
$response->send();
}
}
并对控制器进行如下修改:
$output = new StreamedOutput();
结果是页面在命令执行后立即流式传输命令的输出(而不是等待它完成)。
您将必须实现自己的输出 class 并注入它而不是 BufferedOutput。您可以扩展抽象 class \Symfony\Component\Console\Output\Output
或实现接口 vendor/symfony/symfony/src/Symfony/Component/Console/Output/OutputInterface.php:20
.
您的实施应该像此处描述的那样流式传输响应:http://symfony.com/doc/current/components/http_foundation/introduction.html#streaming-a-response 而不是等待命令执行结束并将所有输出一起发回。当然,只有当您的命令在执行过程中打印出某些内容时,这才会起作用。