实战 php 使用 wkhtmltopdf 生成pdf的全过程

2024-01-12 21:12

本文主要是介绍实战 php 使用 wkhtmltopdf 生成pdf的全过程,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

在这里插入图片描述

公司里边有生成pdf报告的业务需求,之前有过尝试用tcpdf,直接生成的pdf的过程,但是pdf报告的内容数据,根据不同内容的变化,都是各种各样的bug,一直处理修修补补的状态,让后台开发人员很是头疼.

经过思索和甄选,总结出我们的业务中是由于样式不可控导致的,当时从逻辑上就思考到用html转pdf的思维,后边在搜索中发现wkhtmltopdf(一下简称wk)和其他几款软件及插件的比较,决定选用wk,是由于样式灵活可控,并且我们都没有js动态加载的数据,只是常规的数据渲染到html页面中,然后导出pdf,上手使用后,发现带来了很好的效果,

  • 1.解决了以往pdf报告容量大的弊病
  • 2.解决了出报告效率的问题.
  • 3.提升开发效率
  • 4.适用于windows和linux平台

我们的操作流程是:

web页面中发起请求---->生成一个经过数据渲染的静态页面--->调用wk生成pdf--->返回生成pdf报告地址--->为使用者提供下载入口

wk可以直接把任何一个可以在浏览器中浏览的网页直接转换成一个pdf,首先说明一下它不是一个php 类,而是一个把html页面转换成pdf的一个软件(需要安装在服务器上),但是它并不是一个简单的桌面软件,而且它直接cmd批处理的,使用php中的 shell_exec()函数就可以调用它。下面就介绍如何用php+js+html来让它生成pdf文件的方法(不过有个缺陷就是他需要在服务器端生成一个缓存文件,如果你使用thinkphp框架的话就可以将其缓存文件放在runtime 文件夹中暂存就行)。

  • 下载地址:http://wkhtmltopdf.org/downloads.html

栗子:使用1

<?php
//转成pdf
$html=$_POST['html'];
//Turn on output buffering
ob_start();
$html='<link rel="stylesheet" href="css/common.css" rel="external nofollow" rel="external nofollow" >;
<link rel="stylesheet" href="css/myCenter.css" rel="external nofollow" rel="external nofollow" >;
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" >;'.$html;
//这儿可以引入生成的Html的样式表 路径可以是绝对路径也可以是相对路径,也可以把样式表文件复制到临时html文件的目录下 即这儿的demo文件目录下(默认) 也可以直接把样式写在html页面中直接传递过来
//$html = ob_get_contents();
//$html=$html1.$html;
$filename = "hld";
//save the html page in tmp folder 保存的html临时文件位置 可以是相对路径也是可以是绝对路径 下面用相对路径
file_put_contents("{$filename}.html", $html);
//Clean the output buffer and turn off output buffering
ob_end_clean();
//convert HTML to PDF
shell_exec("wkhtmltopdf -q {$filename}.html {$filename}.pdf");
if(file_exists("{$filename}.pdf")){header("Content-type:application/pdf");header("Content-Disposition:attachment;filename={$filename}.pdf");echo file_get_contents("{$filename}.pdf");//echo "{$filename}.pdf";
}else{exit;
}
>;<div>使用2:</div>
<div>
<pre>phpwkhtmltopdf类库的使用,</pre>
<pre><code>composer require mikehaertl/phpwkhtmltopdf</code></pre>
</div>

———————————分割线———————————

以下是其他朋友在客户端中运用的思路:

一年前产品提了一个让我们试着实现看看的需求,前端要支持用户将系统中java动态页面一键导出为pdf文件。当时项目组决定使用wkhtmltopdf这个第三方软件来实现。当时是由一位后端开发的同学来实现的,逻辑是前端点击按钮,后端生成动态文件,调用wkhtmltopdf生成pdf文件,存储在服务器中,并将文件地址返回给前端进行调用下载,并且要加上页眉页脚(页眉中包含客户商标)。逻辑图如下:

发送请求–>生成动态文件–>调用wkhtmltopdf生成pdf–>返回pdf地址–>下载pdf文件

总算是试着实现了,后来理所当然的三番五次接到各种生成pdf的需求了,wkhtmltopdf在系统中也开始渐渐“活跃”了起来,同时也bug不断。再后来, 由于人员变动,我承接了使用wkhtmltopdf的全部bug。

wkhtmltopdf相信用过的人大多数用过的人对其实又爱又恨。优缺点不多说了,用过的自然会明白。简单总结就是:功能强大,漏洞百出。

———————————分割线———————————

最近接到一个新需求,在动态页面中的过程变量(就是各种交互组件,涉及到产品保密不做多描述)需要支持用户设置宽高,其中图片有一种配置是上传后,如果配置了默认,则显根据图片原始大小显示。最近在对前端项目进行改造升级,毫不犹豫的使用了React作为输出渲染,功能实现的很愉快。

最终,从测试那里接到噩耗,生成pdf内容不全,甚至说没有内容。鉴于wkhtmltopdf的使用者较少,或者用来生成文件的目标页面几乎都比较简单,网上也没有太多的相关问题解答,于是本老中医根据病情列出了如下可能进行排查:

javascript脚本执行太慢导致部分dom元素无法加载
wkhtmltopdf版本太低
图片以及其他元素动态缩放引起的reflow导致页面加载迟缓
wkhtmltopdf不支持动态页面或者react
第一个猜想很快被排除了,除了调整–javascript-delay参数以外,各种参数调整都无效
第二个猜想也很快排除,wkhtmltopdf已经停止维护好久了(年中的时候本打算替换为phantomjs来实现生成pdf的,结果刚要决定就接到其核心成员退出项目停止维护的噩耗)
第三个测试比较费力气,单个搭建动态页面进行测试,排除每一种动态缩放的可能,但是最终还是一样
第四个测试简单粗暴,先删除了react以及相关组件的引用,居然成功了。注意这里只测试了react。于是当前晚上大张旗鼓,对前三天的功能进行重构,使用传统的javascript对图片过程变量进行了重构,好在轻车熟路,一晚上加上第二天一上午搞定了,编译+测试。。。。。。失败最后冥思苦想,突然想到把动态页面里面的资源拷贝出来,存储成html文件在本地调用生成pdf,成功了!。在请教了部门老大(公司大牛)后,决定使用静态化过程进行处理,方案如下:
发起请求

生成动态页面—>返回动态页面—>获取动态资源—>中间层服务存储为html—>将静态化url传递给后端—>生成pdf并下载

完美解决问题。不过为了赶项目,方案解决的优点仓促。有个弊端就是上行数据量太大,要修改中间层服务器的上传限制。完美的方案是应该把页面静态化和打印访问全部做在中间层中。当然这部分后来慢慢优化吧。

总结:

wkhtmltopdf对动态页面的支持效果一般,动态页面输出pdf的需求中最好不要有太复杂的javascript逻辑,尤其是一些比较大的视图框架(至少react是的)。加载的js文件最好不要太大。如果可以最好先静态化再输出。对于输出页面的样式,尤其是圆角等css3特性,能少则少,否则缺失个线条啥的都是家常便饭

参考

  • https://blog.csdn.net/qq_20867249/article/details/84643252

这篇关于实战 php 使用 wkhtmltopdf 生成pdf的全过程的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/599174

相关文章

C语言小项目实战之通讯录功能

《C语言小项目实战之通讯录功能》:本文主要介绍如何设计和实现一个简单的通讯录管理系统,包括联系人信息的存储、增加、删除、查找、修改和排序等功能,文中通过代码介绍的非常详细,需要的朋友可以参考下... 目录功能介绍:添加联系人模块显示联系人模块删除联系人模块查找联系人模块修改联系人模块排序联系人模块源代码如下

Java中使用Java Mail实现邮件服务功能示例

《Java中使用JavaMail实现邮件服务功能示例》:本文主要介绍Java中使用JavaMail实现邮件服务功能的相关资料,文章还提供了一个发送邮件的示例代码,包括创建参数类、邮件类和执行结... 目录前言一、历史背景二编程、pom依赖三、API说明(一)Session (会话)(二)Message编程客

C++中使用vector存储并遍历数据的基本步骤

《C++中使用vector存储并遍历数据的基本步骤》C++标准模板库(STL)提供了多种容器类型,包括顺序容器、关联容器、无序关联容器和容器适配器,每种容器都有其特定的用途和特性,:本文主要介绍C... 目录(1)容器及简要描述‌php顺序容器‌‌关联容器‌‌无序关联容器‌(基于哈希表):‌容器适配器‌:(

C#提取PDF表单数据的实现流程

《C#提取PDF表单数据的实现流程》PDF表单是一种常见的数据收集工具,广泛应用于调查问卷、业务合同等场景,凭借出色的跨平台兼容性和标准化特点,PDF表单在各行各业中得到了广泛应用,本文将探讨如何使用... 目录引言使用工具C# 提取多个PDF表单域的数据C# 提取特定PDF表单域的数据引言PDF表单是一

使用Python实现高效的端口扫描器

《使用Python实现高效的端口扫描器》在网络安全领域,端口扫描是一项基本而重要的技能,通过端口扫描,可以发现目标主机上开放的服务和端口,这对于安全评估、渗透测试等有着不可忽视的作用,本文将介绍如何使... 目录1. 端口扫描的基本原理2. 使用python实现端口扫描2.1 安装必要的库2.2 编写端口扫

使用Python实现操作mongodb详解

《使用Python实现操作mongodb详解》这篇文章主要为大家详细介绍了使用Python实现操作mongodb的相关知识,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录一、示例二、常用指令三、遇到的问题一、示例from pymongo import MongoClientf

SQL Server使用SELECT INTO实现表备份的代码示例

《SQLServer使用SELECTINTO实现表备份的代码示例》在数据库管理过程中,有时我们需要对表进行备份,以防数据丢失或修改错误,在SQLServer中,可以使用SELECTINT... 在数据库管理过程中,有时我们需要对表进行备份,以防数据丢失或修改错误。在 SQL Server 中,可以使用 SE

使用Python合并 Excel单元格指定行列或单元格范围

《使用Python合并Excel单元格指定行列或单元格范围》合并Excel单元格是Excel数据处理和表格设计中的一项常用操作,本文将介绍如何通过Python合并Excel中的指定行列或单... 目录python Excel库安装Python合并Excel 中的指定行Python合并Excel 中的指定列P

浅析Rust多线程中如何安全的使用变量

《浅析Rust多线程中如何安全的使用变量》这篇文章主要为大家详细介绍了Rust如何在线程的闭包中安全的使用变量,包括共享变量和修改变量,文中的示例代码讲解详细,有需要的小伙伴可以参考下... 目录1. 向线程传递变量2. 多线程共享变量引用3. 多线程中修改变量4. 总结在Rust语言中,一个既引人入胜又可

golang1.23版本之前 Timer Reset方法无法正确使用

《golang1.23版本之前TimerReset方法无法正确使用》在Go1.23之前,使用`time.Reset`函数时需要先调用`Stop`并明确从timer的channel中抽取出东西,以避... 目录golang1.23 之前 Reset ​到底有什么问题golang1.23 之前到底应该如何正确的