Laravel5.8 使用 snappyPDF 生成PDF文件

  • 背景

    最近在设计网站功能时,想要将模板页面生成 pdf 文件,方便用户下载使用

    对比 domPdf 发现 snappyPdf 的性能更满足我的需求

    在此 进行配置

  • 概况

    wkhtmltopdf 是一个开源项目,它的目的是为了解决网页转 PDF 的问题。

    它的核心是一个命令行工具,可以将 HTML 内容渲染为 PDF 格式。

    由于它使用了 Webkit 引擎,因此能够高度保真地转换 HTML 内容,包括 CSS 样式和 JavaScript 动态效果。

    Snappy 是基于 wkhtmltopdf/wkhtmltoimage 的 PHP 封装库,提供以下核心能力:

  • 功能对比

曾对 同一个合同页面 生成pdf文件时,对比发下:

domPdf 生成时间长,文件大小超过15M,且部分css样式需调整;但 snappy 明显快的多,且文件大小仅有105KB,渲染效果好

b 复制代码
PHP版本:php 7.2.3
Laravel 版本:Laravel5.8

安装步骤

根据网上经验,结合鄙人的实际操作,在此记录一下 整体的安装使用步骤...

① 安装 wkhtmltopdf 和 SnappyPDF 库

通过 网上经验发现,最为匹配的 版本为:0.4.8

  • config/snappy.php 中配置 wkhtmltopdf 的路径。
    通常情况下,需要指定 wkhtmltopdf 可执行文件的路径,Laravel Snappy会自动去这个路径寻找。

a. window系统

  • snappy 库依赖安装配置
bash 复制代码
composer require barryvdh/laravel-snappy:0.4.8
  • 如果是在Windows系统需要安装 wkhtmltopdf。

    可以从 wkhtmltopdf 的官方 GitHub 页面下载预编译的二进制文件。选择适合的 Windows 版本的版本下载。

  • 因为官网有时下载效率太慢,提供下载分享 【 >>> 链接: 软件分享 wkhtmltopdf (提取码: 8fj9)

  • 下载后,解压文件,并将解压后的文件夹路径添加到系统的环境变量【PATH】中,这样你就可以在命令行中直接使用 wkhtmltopdf 命令。(可百度查询配置方式)

  • 需要指定完整的wkhtmltopdf的可执行文件路径。例如:'binary' => 'D:\\software\\wkhtmltopdf\\bin\\wkhtmltopdf.exe',

b. Linux或Mac系统 (以 Linux (CentOS7)操作系统,执行配置)

  • snappy 库依赖安装配置
bash 复制代码
composer require h4cc/wkhtmltopdf-amd64
composer require barryvdh/laravel-snappy:0.4.8
  • 对于Linux 或 Mac 系统,可能需要通过包管理器安装 wkhtmltopdf,然后填写相应的路径。
bash 复制代码
#下载 wkhtmltopdf
[root@applet-server opt]# wget https://github.com/wkhtmltopdf/wkhtmltopdf/releases/download/0.12.4/wkhtmltox-0.12.4_linux-generic-amd64.tar.xz
  #解压:使用 tar xvfJ 命令解压
[root@applet-server opt]# tar xvfJ wkhtmltox-0.12.4_linux-generic-amd64.tar.xz
#cd到wkhtmltox/bin目录下
[root@applet-server opt]# cd wkhtmltox/bin

#将wkhtmltox/bin目录下的"wkhtmltopdf"命令文件拷贝到 /usr/local/bin/下
[root@applet-server wkhtmltopdf]# cp ./wkhtmltopdf /usr/local/bin/wkhtmltopdf
[root@applet-server wkhtmltopdf]# cp ./wkhtmltoimage /usr/local/bin/wkhtmltoimage

#添加执行权限
[root@applet-server wkhtmltopdf]# cd /usr/local/bin/
[root@applet-server bin]# chmod +x ./wkhtmltopdf
[root@applet-server bin]# chmod +x ./wkhtmltoimage

[root@applet-server bin]# chmod 777 ./wkhtmltopdf
[root@applet-server bin]# chmod 777 ./wkhtmltoimage

//测试 ,如果在/usr/local/bin/下生成"test.pdf"则表示成功
[root@applet-server bin]# wkhtmltopdf http://www.baidu.com ./test.pdf

③. 配置 Laravel

  • app.php 补充配置信息如下:
php 复制代码
//添加服务提供者
'providers' => [    
	Barryvdh\Snappy\ServiceProvider::class,
],

//添加门面
'aliases' => [    
	'SnappyPDF' => Barryvdh\Snappy\Facades\SnappyPdf::class,
	]
  • config/snappy.php 配置信息如下:
php 复制代码
<?php

return [
    'pdf' => [
        'enabled' => true,
        'binary' => '/usr/local/bin/wkhtmltopdf',
        //'binary'  => 'D:\\software\\wkhtmltopdf\\bin\\wkhtmltopdf.exe', //windows安装路径
        'timeout' => false,
        'options' => [],
        'env'     => [],
    ],
    'image' => [
        'enabled' => true,
        'binary'  => '/usr/local/bin/wkhtmltopdf',
        'timeout' => false,
        'options' => [],
        'env'     => [],
    ],
];

④. 使用 SnappyPDF

  • 创建一个控制器方法来生成 PDF:
php 复制代码
<?php
 
namespace App\Http\Controllers;
 
use PDF; // 使用 Facade
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
 
class PdfController extends Controller
{
    public function generatePdf()
    {
    	//...
        $pdf = PDF::loadView('pdf.invoice', $view_data); // 加载视图和数据生成 PDF
        return $pdf->download('invoice.pdf'); // 返回下载的 PDF 文件
    }
}
  • 对应的模板文件 pdf.invoice.blade,参考示例代码如下:
php 复制代码
<!-- pdf-with-image.blade.php -->
<img src="{{ $imagePath }}" alt="示例图片" />

⑤. 清除缓存(可选)

  • 如果你在配置过程中遇到问题,尝试运行以下命令来清除 Laravel 的配置缓存:
bash 复制代码
php artisan config:clear
php artisan cache:clear

附录

1. 性能优化的方法和策略

  • 除了缓存,还可以通过以下策略进一步优化PDF生成的性能:
b 复制代码
减少外部资源加载 :尽量减少PDF中加载外部图像或CSS的次数,因为这些请求会增加生成PDF的时间。
使用队列处理 :对于大量或复杂的PDF生成任务,可以使用Laravel的队列系统异步处理,避免阻塞主进程。
优化HTML模板 :确保生成PDF所用的HTML模板尽可能简洁高效,避免不必要的DOM结构。

2. 支持动态内容的方法和步骤

  • 要支持动态内容,例如JavaScript执行后的元素或实时数据,可以使用 --load-error-handling 和 --no-sandbox 选项。
php 复制代码
$html = view('dynamic-content', [...]);
// 调用 wkhtmltopdf 的命令行选项
$cmdOptions = ['--load-error-handling', 'ignore'];
$this->pdf->setOptions($cmdOptions);
return $this->pdf->getOutputFromHtml($html);

3. 发现如果表格数据特别多,分页后,可能会出现 跨页的顶部导航栏与表格数据重合

  • 测试发现,有个最简单的解决方案:你可以通过 CSS 来优化表格的布局,使其更适合跨页显示
html 复制代码
<style type="text/css">
        th, td, tr {
            page-break-inside: avoid; /* 防止内容跨页断裂 */
        }
</style>

4. 辅助指令

b 复制代码
#查看wkhtmltopdf的版本
[root@b1bweb-uat uat.b1b.com]# wkhtmltopdf --version
wkhtmltopdf 0.12.4 (with patched qt)

#查看 wkhtmltopdf 的命令路径
[root@b1bweb-uat uat.b1b.com]# whereis wkhtmltopdf
wkhtmltopdf: /usr/local/bin/wkhtmltopdf

#查看已安装的字体
[root@b1bweb-uat uat.b1b.com]# fc-list :lang=zh
/usr/share/fonts/simsun.ttf: SimSun:style=Regular