从 HTML 生成 PDF 仅在使用本地资源时需要很长时间
generating a PDF from HTML takes a long time only when using local assets
我使用 KnpSnappyBundle 1.6.0 和 wkhtmltopdf 0.12.5 从 PHP 中的 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 的组合)。我是 运行ning Nginx-1.16.1 并通过 SSL 提供所有内容。我安装了 OpenSSL 1.1.1d 2019 年 9 月 10 日(库:OpenSSL 1.1.1g 2020 年 4 月 21 日),我的 OS 是 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
}
2020 年 8 月 5 日更新: 我尝试了 wkhtmltopdf 0.12.6,但这给了我完全相同的问题。几个月前我作为问题的答案发布的“解决方案”远非完美,这就是我寻找新建议的原因。感谢任何帮助。
我还没有找到问题的根源。但是,我找到了解决方法。我所做的是:
全局安装 wkhtmltopdf(由我的发行版提供):
sudo apt-get install wkhtmltopdf
这会通过 Ubuntu 存储库安装 wkhtmltopdf 0.12.4(2019 年 11 月 5 日)。这是 wkhtmltopdf 的旧版本, 运行 单独使用它给我带来了无数问题。为了解决这个问题,我现在 运行 它在 xvfb 里面。首先通过 运行ning:
安装
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 文件。我将所有 CSS 引用转换为直接包含。我以编程方式执行此操作,因此我仍然可以将它们作为单独的工具文件保存。如果您创建一个辅助方法以根据 URI 包含它们,这将非常简单。同样,我尝试对所有图像进行 base64 编码,并将这些图像也包含在内。同样,我将它们保存为单独的文件并以编程方式执行此操作。
然后我将这个“self-contained”html 提供给 wkhtmltopdf。
我会分享一些示例,但我的实现实际上是 C# 和 Razor。
除此之外,如果您仍然遇到问题,我还会使用时间戳对这些帮助程序进行一些登录,这样您就可以看到包含所花费的时间。
我不确定服务器设置是什么,但可能是连接到 NAS 或其他问题。
您也可以在其余步骤周围添加一些带有时间戳的日志记录,以确切了解哪些步骤花费了很长时间。
其他提示,我尝试对图像使用 SVG(在可能的情况下),并且尽量不要将大型(或任何)CSS 库拉入成为 pdf 的 html。
我觉得这像是 DNS 问题。我会尝试在 /etc/hosts
中添加一个条目,例如:
127.0.0.1 example.com
127.0.0.1 www.example.com
并指向您的图像以使用该域
我使用 KnpSnappyBundle 1.6.0 和 wkhtmltopdf 0.12.5 从 PHP 中的 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 的组合)。我是 运行ning Nginx-1.16.1 并通过 SSL 提供所有内容。我安装了 OpenSSL 1.1.1d 2019 年 9 月 10 日(库:OpenSSL 1.1.1g 2020 年 4 月 21 日),我的 OS 是 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
}
2020 年 8 月 5 日更新: 我尝试了 wkhtmltopdf 0.12.6,但这给了我完全相同的问题。几个月前我作为问题的答案发布的“解决方案”远非完美,这就是我寻找新建议的原因。感谢任何帮助。
我还没有找到问题的根源。但是,我找到了解决方法。我所做的是:
全局安装 wkhtmltopdf(由我的发行版提供):
sudo apt-get install wkhtmltopdf
这会通过 Ubuntu 存储库安装 wkhtmltopdf 0.12.4(2019 年 11 月 5 日)。这是 wkhtmltopdf 的旧版本, 运行 单独使用它给我带来了无数问题。为了解决这个问题,我现在 运行 它在 xvfb 里面。首先通过 运行ning:
安装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 文件。我将所有 CSS 引用转换为直接包含。我以编程方式执行此操作,因此我仍然可以将它们作为单独的工具文件保存。如果您创建一个辅助方法以根据 URI 包含它们,这将非常简单。同样,我尝试对所有图像进行 base64 编码,并将这些图像也包含在内。同样,我将它们保存为单独的文件并以编程方式执行此操作。
然后我将这个“self-contained”html 提供给 wkhtmltopdf。
我会分享一些示例,但我的实现实际上是 C# 和 Razor。
除此之外,如果您仍然遇到问题,我还会使用时间戳对这些帮助程序进行一些登录,这样您就可以看到包含所花费的时间。
我不确定服务器设置是什么,但可能是连接到 NAS 或其他问题。
您也可以在其余步骤周围添加一些带有时间戳的日志记录,以确切了解哪些步骤花费了很长时间。
其他提示,我尝试对图像使用 SVG(在可能的情况下),并且尽量不要将大型(或任何)CSS 库拉入成为 pdf 的 html。
我觉得这像是 DNS 问题。我会尝试在 /etc/hosts
中添加一个条目,例如:
127.0.0.1 example.com
127.0.0.1 www.example.com
并指向您的图像以使用该域