仅在使用本地资源时从 HTML 生成 PDF 需要很长时间

Posted

技术标签:

【中文标题】仅在使用本地资源时从 HTML 生成 PDF 需要很长时间【英文标题】:generating a PDF from HTML takes a long time only when using local assets 【发布时间】:2020-02-29 15:11:50 【问题描述】:

我使用 KnpSnappyBundle 1.6.0wkhtmltopdf 0.12.5php 中从 html 生成 PDF,如下所示:

$html = $this->renderView(
    'pdf/template.html.twig',
    [ 'entity' => $entity, ]
);

return new PdfResponse($snappy->getOutputFromHtml($html,
    ['encoding' => 'UTF-8', 'images' => true]), 'file'.$entity->getUniqueNumber().'.pdf'
);

我的问题: 在我的生产服务器上,当我引用托管在与我的代码相同的服务器上的资产(图像或 css)时,生成 PDF 大约需要 40-50 秒。即使我只使用托管在同一台服务器上的小图像,也需要 40 秒。我可以使用托管在另一台服务器上的更大的图像,并且会立即生成 PDF。

我的服务器在服务资产或文件方面并不慢。如果我只是将 HTML 呈现为一个页面,它会立即发生(有或没有资产)。当我在本地(在我的笔记本电脑上)从我的生产服务器请求资产以生成 PDF 时,它也会立即发生。

我需要的 HTML 中需要呈现为 PDF 的资产都具有绝对 URL,这是 wkhtmltopdf 工作所必需的。例如:<img src="https://www.example.com/images/logo.png"> 困难的是,一切正常,但速度很慢。没有指向会导致超时的不存在资产。

我一开始以为可能和wkhtmltopdf有关,所以我尝试了不同的版本和不同的设置,但这并没有改变任何东西。我还尝试指向同一服务器上的另一个域,问题仍然存在。我尝试不使用 KnpSnappyBundle,但问题仍然存在。

所以我现在的猜测是这是一个服务器问题(或与 wkhtmltopdf 的组合)。我正在运行 Nginx-1.16.1 并通过 SSL 提供所有内容。我已安装 OpenSSL 1.1.1d 2019 年 9 月 10 日(库:OpenSSL 1.1.1g 2020 年 4 月 21 日),我的操作系统是 Ubuntu 18.04.3 LTS。其他一切都在此服务器上按预期工作。

当我查看 nginx 访问日志时,我可以看到在使用来自同一服务器的资产时,我自己的 IP 地址发出了一个获取请求。我不明白为什么这要花这么长时间,而且我已经不知道接下来要尝试什么了。任何想法表示赞赏!

我将为我的域添加我的 Nginx 配置(以防万一):

server 
        root /var/www/dev.example.com/public;
        index index.php index.html index.htm index.nginx-debian.html;

        server_name dev.example.com www.dev.example.com;

        location / 
        # try to serve file directly, fallback to index.php
        try_files $uri /index.php$is_args$args;     
    

        location ~ ^/index\.php(/|$) 
        fastcgi_pass unix:/var/run/php/php7.3-fpm.sock;
        fastcgi_split_path_info ^(.+\.php)(/.*)$;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
        fastcgi_param DOCUMENT_ROOT $realpath_root;
        internal;
    

  location ~ \.(?:jpg|jpeg|gif|png|ico|woff2|cur|gz|svg|svgz|mp4|ogg|ogv|webm|htc|js|css)$ 
        gzip_static on;

        # Set rules only if the file actually exists.
        if (-f $request_filename) 
        expires max;
        access_log off; 
        add_header Cache-Control "public";
    
            try_files $uri /index.php$is_args$args;     
 

    error_log /var/log/nginx/dev_example_com_error.log;
    access_log /var/log/nginx/dev_example_com_access.log;

    listen 443 ssl; # managed by Certbot
    ssl_certificate /etc/letsencrypt/live/dev.example.com/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/dev.example.com/privkey.pem; # managed by Certbot
    include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot


server 
    if ($host = dev.example.com) 
        return 301 https://$host$request_uri;
     # managed by Certbot

    server_name dev.example.com www.dev.example.com;
    listen 80;
    return 404; # managed by Certbot

Udate 2020 年 8 月 5 日:我尝试了 wkhtmltopdf 0.12.6,但这给了我完全相同的问题。几个月前我发布的作为我的问题的答案的“解决方案”远非完美,这就是我寻找新建议的原因。任何帮助表示赞赏。

【问题讨论】:

很难说。您可以尝试不使用 Knp 捆绑包吗?试试这个github.com/mikehaertl/phpwkhtmltopdf。这是我关于包的博客笔记delboy1978uk.wordpress.com/2014/11/24/… @delboy1978uk,感谢您的建议。我用那个捆绑包试过了,但我有完全相同的问题。很高兴知道我可以排除 Knp Bundle。 如果输出正确,但生成时间太长,那么我认为问题是 1/ https - 对等 SSL 验证或 2/ CLI 模式下的 DNS 主机名查找。修复 1/ 尝试将所有链接从 https 切换到 http。对于 2/ 检查在 CLI 模式下看到 PHP 的服务器名称。不要信任 HTTP 模式 - 可能使用不同的 php.ini / env 变量。最终正确定义 etc/hostname。 @lubosdz, http 没有改变。当我在 cli 中使用 PHP(使用php -a)并运行echo gethostname(); 时,它会显示我的名字server,这与/etc/hostname 中的相同。 @DirkJ.Faber 1. 由于我们仍然怀疑资产存在问题,您是否尝试过在不包含任何资产的情况下生成 PDF? 2. 从服务器本身检查您的一项资产上的response time from curl。 3. 如果可能,尝试在您呈现的 HTML 页面上直接从 wkhtmltopdf CLI 生成。 【参考方案1】:

对我来说,这听起来像是一个 DNS 问题。我会尝试在/etc/hosts 中添加一个条目,例如:

127.0.0.1     example.com
127.0.0.1     www.example.com

并指向您的图像以使用该域

【讨论】:

这个解决方案很简单,它对我有用!奇怪的是,如果我的 hosts 文件中没有这些行,我没有遇到任何其他问题,而服务器已经启动并运行了很长一段时间。【参考方案2】:

不确定这是否适合您,但就我而言,我总是生成一个可以独立存在的 HTML 文件。我将所有 CSS 引用转换为直接包含在内。我以编程方式执行此操作,因此我仍然可以将它们保存为单独的工具文件。如果您基于 URI 创建一个辅助方法来包含它们,这将是相当简单的。同样,我尝试对所有图像进行 base64 编码并包含这些图像。同样,我将它们保存为单独的文件并以编程方式执行此操作。

然后我将这个“自包含”的 html 提供给 wkhtmltopdf。

我会分享一些示例,但我的实现实际上是 C# 和 Razor。

除此之外,如果您仍然遇到问题,我还会在这些助手中构建一些带有时间戳的日志记录,这样您就可以看到包含需要多长时间。

我不确定服务器设置是什么,但可能是连接到 NAS 或其他问题。

您还可以在其余步骤周围添加一些带有时间戳的日志记录,以准确了解哪些步骤需要很长时间。

其他提示,我尝试使用 SVG(在我可以的情况下)用于图像,并尽量不要将大型(或任何)CSS 库拉入成为 pdf 的 html 中。

【讨论】:

【参考方案3】:

我还没有找到问题的根源。但是,我找到了一种解决方法。我所做的是:

全局安装wkhtmltopdf(由我的发行版提供):

sudo apt-get install wkhtmltopdf 

这将通过 Ubuntu 存储库安装 wkhtmltopdf 0.12.4(2019 年 11 月 5 日)。这是 wkhtmltopdf 的旧版本,运行它本身给我带来了无数问题。为了解决这个问题,我现在在 xvfb 中运行它。首先通过运行安装它:

sudo apt-get install xvfp

然后将您使用的指向 wkhtmltopdf 的包装器的二进制路径更改为:

'/usr/bin/xvfb-run /usr/bin/wkhtmltopdf' 

就我而言,我使用 KnpSnappyBundle 并在我的.env 文件中设置二进制路径在knp_snappy.yaml 我设置binary: '%env(WKHTMLTOPDF_PATH)%'.env 我设置WKHTMLTOPDF_PATH='/usr/bin/xvfb-run /usr/bin/wkhtmltopdf'(如所述更多)。尽管布局存在一些问题,但我现在可以生成 PDF。

【讨论】:

以上是关于仅在使用本地资源时从 HTML 生成 PDF 需要很长时间的主要内容,如果未能解决你的问题,请参考以下文章

仅在需要时从文件中进行 pip 安装

《html转pdf-----wkhtmltopdf踩坑总结》

Laravel 将生成的 Pdf 保存在队列中

从 asp.net 中的字节生成 pdf [关闭]

如何确保在生成 PDF 之前加载图像?

根据图片的URL生成PDF保存到本地(前台js)