项目中要求导出pdf报告,之前使用的canvas转pdf但是当报告太大的时候会出问题,所以换成了后台去处理。wkhtmltopdf从名字来看就一目了然,使用webkit把html渲染成pdf。由于水平很菜所以自己在开发过程中不断掉坑,现在总结一下希望能帮助到遇到同样问题的小伙伴。
1.安装篇
wkhtmltopdf 可谓是功能强大,漏洞百出,可能不是他的原因,毕竟依赖webkit外部程序。所以在我们下载的时候就要看好版本了,以ubuntu安装为例,命令行输入lsb_release -a
可以看到Codename:bionic,去wkhtmltopdf官网下载指定版本的deb包,如果是远程服务器,直接wget到服务器就好了,然后sudo apt install
安装软件包。下载页面有这么一句话:
所以让我们遵循规定在下载下来xvfb sudoo apt install xvfb
,这样环境就准备好了,我们最后的目的就会在xvfb环境下执行wkhtmltopdf命令。
2.使用篇 先拿百度做例子如果你人品够好那么简单的很xvfb-run wkhtmltopdf www.baidu.com baidu.pdf
,你会在当前目录收获一个pdf文件打开就是百度首页生成的pdf。这时候就万事具备了,但是很多时候我们去转换的页面是一个复杂的动态页面,有复杂的javascript需要在转换前执行,wkhtmltopdf -H
查看文档在page options中有个--window-status <...>
参数,这个参数的意思就是只有当你的js程序中显式的调用window.status = <…> 只有值相同的时候wkhtmltopdf才会去渲染页面,这是wkhtmltopdf的一个黑科技呀。另外如果你仔细看文档会发现它还支持读写cookies所以遇到有权限的页面也不用怕了。
3.调用篇
接下来我们的目的是用golang调用wkhtmltopdf,github上倒是有对应的库但是一条命令完成的事就不要再麻烦的找第三方库了,不要给自己的程序引入太多的依赖,不然容易出问题。下面代码做的事情是根据用户给定的url在本地生成一个pdf文件,并且只有当js中window.status='ready'
的时候才去渲染页面
func generateExec(url string, pdfFile string) error {
c := "xvfb-run wkhtmltopdf --window-status ready " + htmlFile + " " + pdfFile
cmd := exec.Command("sh", "-c", c)
if err := cmd.Run(); err != nil {
return err
}
return nil
}
我们调用这个函数generateExec("www.baidu.com","aim.pdf")
,所以现在我们本地有了一个名字叫做aim.pdf的文件,现在的目标是把这个文件传给前台程序。在golang中简单的很,三行搞定。
c.Writer.Header().Add("Content-Disposition", fmt.Sprintf("attachment; filename=%s", fileName))
c.Writer.Header().Add("Content-Type", "application/octet-stream")
c.File(fileName)
c.File => File writes the specified file into the body stream in a efficient way.
从c.File的函数注释来看,访问的请求应该是post
。
4 前段篇
现在后台已经Ok了我们只需要创建个请求去访问借口就好了,假设你现在使用的是vue+axios,那么注意两点1.访问方式为post2相应类型设置为arraybuffer,注意在我这个函数中this.$route.param.id 、测试报告.pdf
都是我自己的配置,你需要换成自己的。
axios({
method: 'post',
url: process.env.API_BASE + '/version/shared-report-byte',
data: { id: this.$route.params.id },
responseType: 'arraybuffer',
timeout: 300 // request timeout
}).then(function(response) {
if (!response.data) {
return
}
let url = window.URL.createObjectURL(new Blob([response.data]))
let link = document.createElement('a')
link.style.display = 'none'
link.href = url
link.setAttribute('download', '测试报告.pdf')
document.body.appendChild(link)
link.click()
}).catch(function(error) {
console.log(error)
})
最后的一个步骤就是在要导出的html页面中的你喜欢的地方添加windiw.status='ready'
去控制wkhtmltopdf什么时候去渲染了。
说了这么多还是祝顺利。毕竟wkhtmltopdf功能强大,漏洞百出。