【发布时间】:2019-03-19 06:34:27
【问题描述】:
我有一个奇怪的问题,我已经困扰了几天了。我正在尝试使用 chrome headless 使用此命令在 Laravel 应用程序中生成 pdf google-chrome --headless --disable-gpu --print-to-pdf=outputfile.pdf http://localurl/pdf-html
该命令基本上以无头模式打开 chrome,导航到给定的 url 并将其打印为 pdf,并将文件保存在指定的位置。此命令在我的系统 shell 中运行时运行良好(我使用的是 Ubuntu 18.04)。现在,当尝试从 Laravel 控制器运行相同的命令时出现了我的问题,我尝试了 exec、shell_exec、system 和 passthru,但都给了我同样的问题。如果我在不重定向输出的情况下运行命令并在后台运行进程,则通过将>> tmpfile 2>&1 & 添加到命令末尾,则请求挂起。在后台运行命令通常不会有问题,除了我需要完成命令才能将文件作为下载发送回客户端。通过在后台运行它,这基本上是异步执行它,我无法知道进程何时结束(或等到它结束)然后将文件作为响应的下载发送。
我尝试了其他替代方法,但无济于事。我尝试使用与 Laravel 捆绑在一起的Symfony's Process,但它也失败了。我尝试使用puppeteer,而不是运行 google-chrome 命令,而是使用带有 puppeteer 文档中代码的 node.js 脚本(顺便说一句,当直接在我的系统 shell 中运行时也可以使用),但是从 Laravel 运行时引发 Navigation Timeout Error 异常。
最后我用以下代码创建了一个简单的 php 文件:
<?php
$chromeBinary = 'google-chrome';
$pdfRenderUrl = "http://localhost:8000/pdf-html";
$fileName = 'invoice.pdf';
$outputDirectory = "/path/to/my/file/" . $fileName;
$command = sprintf(
'%s --headless --disable-gpu --print-to-pdf=%s %s',
escapeshellarg($chromeBinary),
escapeshellarg($outputDirectory),
escapeshellarg($pdfRenderUrl)
);
exec( $command );
echo ( file_exists("/path/to/my/file/" . $fileName) ? 'TRUE' : 'FALSE');
?>
当从 shell 运行时,代码运行得很好,比如 php thefile.php 打印 TRUE,这意味着 exec 中的命令已启动,并且在它结束后文件存在;这就是我在 Laravel 上使用的确切代码,只是如上所述,当我将进程发送到后台时它才有效。
有人可以在这里给我留言吗?谢谢
编辑:@namoshek 感谢您的快速回复,如果我没有明确表示抱歉。问题是等待时间不长,也许我可以忍受。问题是 exec 永远不会完成,我最终不得不强制终止进程(也不是 exec,也不是任何其他替代方案,它们都永远完全冻结请求,除了 Process 因抛出 TimeoutException 而失败)。我正在使用邮递员查询端点。前端是一个 Angular 应用程序,这意味着发票下载请求最终将异步进行。此外,任务本身并不是一项长期运行的任务,事实上它完成得很快。对我来说,使用投票策略或通知系统似乎不是一个可行的解决方案。想象一个带有下载按钮的应用程序来下载一个简单的文档,您必须单击该按钮,然后等待应用程序通过电子邮件(或其他方式)通知您文档已准备好。如果它是一个更复杂的过程,我可以理解它,但是文档下载似乎是微不足道的。但是让我不知所措的是,为什么从 php 脚本运行任务可以按我的意愿(同步)工作,而我无法复制 laravel 控制器上的行为
编辑:我也尝试过使用BrowserShot,顺便说一句,它也失败了。 Browsershot 提供了一种在幕后使用 Process 与 puppeteer 交互并生成 pdf 文件的方法。即使它是一个外部程序,在我看来我得到的行为仍然不正常,即使请求需要 10 秒才能完成,我也应该能够获得下载,因为它同步执行了外部程序。但在我的情况下,由于超时错误而失败
编辑:所以过了一会儿,我发现了服务器挂断的明显原因。问题是我使用的是工匠的开发服务器。最初,这对我来说似乎不是问题,但工匠似乎无法处理这种负载。在我正在实现的功能中,我正在对特定端点执行请求,我们称其为端点 1,以生成 pdf,此端点上的代码触发外部命令,当同步执行时,它意味着端点 1 中的代码正在等待外部命令完成。反过来,外部命令需要浏览到同一服务器上的端点 2,端点 2 包含一个 html 视图,其中包含要放在 pdf 上的内容,因为服务器仍在端点 1 上等待外部命令然后端点的返回2 没有响应,这显然会创建一个工匠的开发服务器无法处理的循环。问题是我进行了快速搜索,但没有发现任何迹象表明工匠的开发服务器存在缺陷。我将环境转移到 Apache 只是为了测试我的理论并且它有效,尽管应该注意该请求需要很长时间才能完成(大约 10-20 秒)。到目前为止,这似乎是关于为什么会发生这个问题的唯一合理解释。如果有人知道我可以如何提高此请求的性能,或者任何人都可以对原始问题提供更好的解释,我将不胜感激。
【问题讨论】:
-
好的。两个问题:您是否尝试过在 php-fpm 中使用更高的超时限制(只是为了排除这个原因)?并且webserver用户(php-fpm的用户)是否有权限运行puppeteer等?
-
@namoshek 如果有机会,请查看我的最后一次编辑。无论如何,谢谢你。
标签: php laravel google-chrome pdf exec