html转PDF文件,完美解决方案——jsPDF,htmltocanvas,pdfmake,wkhtmltopdf,TuesPechkin,snappy

最近新换了一个公司,一入职就给了我一个报表下载的项目,从零开始做起。因为之前也做过导出Excel和PDF的相关工作,所以一开始并不觉得有什么困难。直到看到UI设计出的报表之后我的内心是崩溃的。整个报表很长,有各种样式,各种Table。。。这跟用Excel导出一系列数据,或者生成一个简单PDF是很不一样的。因为这两种情况都可以通过后台实现,而且也不复杂。

关于如何通过后台生成一个Excel与PDF这里就不做介绍了,java里都有现成的插件。

先来介绍下我的开发环境:

  • Vue.js
  • Vuex
  • vue-router
  • vue-cli(所以项目构建使用的是webpack)

下面来说说这一路走过来我摔了几次键盘。

第一次摔键盘:

拿到任务第一时间开始到网上找各种方案,参考下各位大牛有没有成熟的解决方案,毕竟这也算是一个合理且应用场景比较广泛的需求。我坚信网上一定会有插件提供类似功能。

过程很顺利,我成功找到了jsPDF,jsPDF是一款能够在前端生成PDF并下载的插件,感觉很牛逼。通过jsPDF与htmltocanvas配合使用就能实现将html页面转换成PDF文件并下载。原理就是通过htmltocanvas给html页面拍个照,然后将页面保存在canvas中,再通过jsPDf将canvas贴到PDF文件中。所以,本质上生成的生成的PDF其实里面就是一张图片。

到这里,一切都很完美。。。但是!只能生成单页PDF!!!这么坑?我是不信的,后来找个一个方案:根据a4纸的高度将生成的canvas图片截成一块一块,然后再分别贴入PDF的不同页面中。这样就能够给生成多页面PDF了。注意:例子里面的调用方法已经过时了,可以参考html2canvas官网的例子。

但是!生成的PDF会被截断!不只是图片会被截断,甚至文字也会被截断。这简直让人无法忍受,于是果断摔键盘,换!方!案!

这里给出几个jsPDF的官方网站,如果键盘多的土豪可以研究一下:

各位老板可别打我,你可能发现网站打不开,于是很心急的买了个VPN,结果发现还是打不开。这时候不要怀疑,没错,后面两个官方的网址就是打!不!开!所以根本没有文档可以参考,甚至有什么API都不知道。。。有前同事很自(chou)信(pi)的说:你可以去看源文件啊。我只能说:嗯嗯,你说的都对。

第二次摔键盘:

第一种方案失败后万念俱灰,因为网上解决生成PDF文件的方案基本上都是使用jsPDF+htmltocanvas。可是这个方案如此不靠谱,不知道还有没有其他更好的方案,心里有点没底。

经过好长时间的查找,终于在一个提问的回答中看到了一个关键字:wkhtmltopdf,于是赶紧搜索。于是找到了一篇介绍wkhtmltopdf与jsPDF优缺点的文章,简直是找到了知音有木有,遇到的问题一样一样的。

找到方案之后立马开始尝试,wkhtmltopdf的执行语法是这样的:

wkhtmltopdf htmlPath ouputPath
复制代码

其中htmlPath是文件路径,可以是网络地址,也可以是本地文件地址。outputPath是导出的PDF文件的存放路径。

在网上找了几个网址,比如CSDN什么的试了一下,还真的可以!生成的PDF文件能够自动分页,唯一的问题就是内容还是会被切分。不过在这之前我已经找到避免分页时,内容被切分的方案了,所以暂时没有理会切分问题,想着回头再解决。具体解决方案我会在接下来的内容里介绍。

有了初步成果之后我开始用我自己的页面做尝试,看看能否顺利转成PDF文件。

。。。。。。。。。耐心等待中。。。。。。。。。

喜闻乐见,失败了。。。文件是生成了,但是里面的内容时一片空白。。。

于是又开始了无尽的google之旅。翻了无数博客之后找了一篇关于wkhtmltopdf导出文件空白的博客,里面给出了一些可能会导致文档空白的例子。我的空白问题并不是这些情况引起的,因此我没有采用这个方案。如何在vue项目中解决空白问题我会单独写一篇博客,这里只介绍一些通用情况。在我的项目中引起空白页的主要原因是就是,使用webpack打包的项目,index.html页面在查看的时候是不显示具体页面内容的,具体内容都包含在一个js文件中,只有在访问到具体某个页面路由的时候相应的资源才会被调出来。但是wkthtmltopdf只能解析静态资源,不会去运行js文件。因此,index.html中包含什么内容,导出来的PDF文件就包含哪些内容。

这里介绍一个判断当前页面能否被wkhtmltopdf正常导出的一个方法:

将当前页面在浏览器中另存为,保存到本地,如果本地文件打开后是有内容的,那么wkhtmltopdf就能正常导出。因为这个工具只会解析html与CSS,并不会去运行js文件。所以在webpack这种项目中,所有资源都被打包成一个js文件,wkhtmltopdf就无法正常导出了。还有一个问题就是:如果页面中的内容时在页面开始渲染时才通过ajax请求从后台获取的,也是无法被渲染出来的,原因还是wkhtmltopdf不会去执行js文件,所以在渲染的时候ajax请求是不会发送的,更加不会被渲染到页面上。

这个问题我的解决方案是:等待页面正常渲染过后将整个页面传给后台,后台在接到页面数据后在本地保存为一个html文件,然后使用wkhtmltopdf将本地html文件转换成PDF文件。不过这样做要注意:一定要在后台提前准备好静态资源,否则在生产环境下CSS样式和图片什么的就无法渲染。

获取页面信息的代码如下:

let htmlEle = document.querySelector('#downloadPaper');
复制代码

为index.html页面的html跟标签附一个id值:downloadPaper,然后通过ajax请求将整个页面信息传递给后台。注意:传送类型为post,参数类型为'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'(具体类型还是要以后台实际为准)。在传参的时候记得取出htmlEle中的html元素:

{
    searchData: htmlEle.innerHTML
}
复制代码

其他也没啥了,后台在成功生成PDF文件后就可以返回文件路径啦,然后前端调用下载接口,将文件路径传递回去就能下载文件啦:

window.open(vm.apiHost + 'download?pdfFilePath=' + res.pdfFilePath)
复制代码

到此为止,已经可以成功下载PDF文件啦,如果你在后台准备了静态文件,生成的PDF文件应该是包含样式的。但是背景图这种较大的图片因为无法被压缩成base64,所以也需要在静态问价那种包含,并将路径配置正确。

为了庆祝一下阶段性成果,我还是要怒砸键盘!因为导出的内容切割问题还没解决。今天写的有点累了,内容切割问题下次再补充吧,休息一会儿。

对了还可以使用Freemarker来生成PDF文件,但是因为一开始把这个方案忘了(其实是不知道怎么在Vue框架中使用),所以就没有研究这种方案。

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值