实战 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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/626382.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

医院如何选择高效的内外网数据交换方案 替代U盘进行跨网传输?

医院信息网络是所有网络中安全性要求较高的网络之一&#xff0c;因此很多医院基于信息安全相关要求&#xff0c;会使用防火墙将网络隔离成内网和外网。内网用于日常医疗信息交换&#xff0c;外网可以及时获取Internet信息资源。但是网络隔离后&#xff0c;医院仍存在将报告资料…

原子类-数组类型原子类

数组类型原子类 AtomicIntegerArray:整型数组原子类 AtomicLongrArray:长整型数组原子类 AtomicReferenceArray:用类型数组原子类 常用API简介 数组类型原子类常用API简介 public final int get(int i) //获取 index=i 位置元素的值 public final int getAndSet(int i, in…

[数据集][目标检测]茶叶病害数据集VOC+YOLO格式883张8类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;883 标注数量(xml文件个数)&#xff1a;883 标注数量(txt文件个数)&#xff1a;883 标注类别…

哪些药物可能对发作性睡病有帮助?

发作性睡病是一种慢性睡眠障碍&#xff0c;其症状包括不可抗拒的短期睡眠发作、猝倒、睡眠麻痹和睡眠幻觉等。治疗发作性睡病的方法包括药物治疗和非药物治疗&#xff0c;其中药物治疗是重要的手段之一。 目前治疗发作性睡病的药物主要包括中枢兴奋剂、抗抑郁药和镇静催眠药等…

【NI国产替代】PXI-6254,32 AI(16位,1 MS/s),48 DIO,PXI多功能I/O模块

32 AI&#xff08;16位&#xff0c;1 MS/s&#xff09;&#xff0c;48 DIO&#xff0c;PXI多功能I/O模块 PXI-6254提供模拟输入、关联数字I/O、两个32位计数器/定时器以及模拟和数字触发。该设备为从实验室自动化、研究、设计验证/测试到制造测试等各种应用提供了低成本的可靠D…

Mybatis基础---------增删查改

增删改 1、新建工具类用来获取会话对象 import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import org.apache.ibatis.io.Resources;import java.io.IOExcept…

Java运算符作用及解析

Java运算符是对变量或者常量进行操作的符号。以下是Java中常见运算符的解析&#xff1a; 赋值运算符&#xff1a;如“”&#xff0c;将右侧的值赋给左侧的变量。一元运算符&#xff1a;如“”“-”“!”&#xff0c;用于对变量进行操作。算术运算符&#xff1a;如“”“-”“*…

典型场景解析|PolarDB分布式版如何支撑SaaS多租户?

SaaS多租户背景 很多平台类应用或系统&#xff08;如电商CRM平台、仓库订单平台等等&#xff09;&#xff0c;它们的服务模型是围绕用户维度&#xff08;这里的用户维度可以是一个卖家或品牌&#xff0c;可以是一个仓库等&#xff09;展开的。因此&#xff0c;这类型的平台业务…

【原创】docker +宝塔+安装zabbix

Zabbix: Zabbix可以监控各种网络服务、服务器和网络设备&#xff0c;而无需在目标设备上安装客户端。它的强大之处在于自带的Web界面&#xff0c;能够提供实时监控和各种报警功能。方法1&#xff1a; 步骤 创建Docker Compose文件: 首先&#xff0c;你需要创建一个docker-comp…

C技能树-学习笔记(1-2)C语言概述和数据类型

参考&#xff1a;https://edu.csdn.net/skill/c 1、输出 “Hello, World!” 字符串&#xff0c;请选出错误答案。 2、错误的print函数。 for … in …&#xff1a;是python的语法&#xff0c;C语言的写法是for (;&#x1f609; 3、C标准 没有C19标准。 4、了解C编译管道 …

AI嵌入式K210项目(4)-FPIOA

文章目录 前言一、FPIOA是什么&#xff1f;二、FPIOA代码分析总结 前言 磨刀不误砍柴工&#xff0c;在正式开始学习之前&#xff0c;我们先来了解下K210自带的FPIOA&#xff0c;这个概念可能与我们之前学习STM32有很多不同&#xff0c;STM32每个引脚都有特定的功能&#xff0c…

嵌入式培训机构四个月实训课程笔记(完整版)-C++和QT编程第三天-C++类和对象高级应用(物联技术666)

链接:https://pan.baidu.com/s/1YRXI0WiABUlYaQXQDNfbyA?pwd=1688 提取码:1688 上午:类和对象高级应用(续) 下午:派生和继承 教学内容: 1、友元 类的私有成员只能在类定义的范围内使用,也就是说私有成员只能通过它的成员函数来访问但是,有时候需要在类的外部访问…

关于前端面试中forEach方法的灵魂7问?

目录 前言 一、forEach方法支持处理异步函数吗&#xff1f; 二、forEach方法在循环过程中能中断吗&#xff1f; 三、forEach 在删除自己的元素后能重置索引吗&#xff1f; 四、forEach 的性能相比for循环哪个好&#xff1f; 五、使用 forEach 会不会改变原来的数组&#…

xshell:关于ssh用户身份验证不能选择password的解决方法

接下来我将告诉大家如何进行修改让其能够进行密码登录 我使用的软件是VM VirtualBox管理器 进行用户名密码登录后 输入 cd /etc/ 切换到etc目录下 cd /etc/ 切换到etc目录后输入ls ls 切换到ssh目录下 cd ssh 进入文件 sshd_config vi sshd_config 找到指定部分进行修改 如何…

多级缓存架构(三)OpenResty Lua缓存

文章目录 一、nginx服务二、OpenResty服务1. 服务块定义2. 配置修改3. Lua程序编写4. 总结 三、运行四、测试五、高可用集群1. openresty2. tomcat 通过本文章&#xff0c;可以完成多级缓存架构中的Lua缓存。 一、nginx服务 在docker/docker-compose.yml中添加nginx服务块。…

评估文字识别准确性的方法与流程

随着信息技术的发展&#xff0c;文字识别技术在各个领域得到了广泛的应用。然而&#xff0c;在实际应用中&#xff0c;如何评估文字识别的准确性&#xff0c;一直是相关领域的一个难题。本文将介绍几种常用的文字识别准确性评估方法&#xff0c;以期为相关领域的研究提供参考。…

使用vite框架封装vue3插件,发布到npm

目录 一、vue环境搭建 1、创建App.vue 2、修改main.ts 3、修改vite.config.ts 二、插件配置 1、创建插件 2、开发调试 3、打包配置 4、package.json文件配置 上一篇文章讲述使用vite《如何使用vite框架封装一个js库&#xff0c;并发布npm包》封装js库&#xff0c;本文将…

Memory Deduplication Attacks

原文 最近看到了一系列描述Memory Deduplication Attacks的研究&#xff0c;它已被用于指纹系统[1]、破解 (K)ASLR[2,3,4]、泄漏数据库记录[4]&#xff0c;甚至利用 rowhammer[ 5]。这是一类非常酷的攻击&#xff0c;以前从未听说过&#xff0c;但我没有太多运气找到这些攻击的…

Jmeter后置处理器——JSON提取器

目录 1、简介 2、使用步骤 1&#xff09;添加线程组 2&#xff09;添加http请求 3&#xff09; 添加JSON提取器 1、简介 JSON是一种简单的数据交换格式&#xff0c;允许互联网应用程序快速传输数据。JSON提取器可以从JSON格式响应数据中提取数据、简化从JSON原始数据中提取特定…

Java学习——Junit单元测试

​​ Junit&#xff1a;事实上的标准单元测试框架 使用Junit&#xff1a;只需要使用 TestCase 和 Assert http://t.csdnimg.cn/hgMFJ