Java 使用 wkhtmltopdf 生成 PDF 遇到的几个坑

wkhtmltopdf 使用本地文件生成 PDF 失败

一般使用命令 wkhtmltopdf URL pdfPath 生成 PDF 文件,其中 URL 为 GET 请求地址。

但是笔者在做的项目是一个模板中心服务(后续代码整理好会上传到 Gitee),实现的功能是可以上传 Thymeleaf 的 HTML 模板文件,然后调用 GET 接口传入模板数据(Map 类型)后,直接返回生成的 PDF。既然使用 Map 传送模板数据,就不能使用 GET 请求了,一方面拼接 Map 比较麻烦,另一方面如果 Map 数据量过大可能超过 URL 长度限制。

印象中 wkhtmltopdf 可以通过本地文件转 PDF,遂尝试先调用 POST 接口将数据填充到模板中,拿到返回的 HTML 后写入到临时文件中,然后通过 wkhtmltopdf HTML pdfPath 将 HTML 文件转换为 PDF 文件。

但是程序怎么弄都不行,就是会报错:

2022-12-13 16:36:59,873 INFO HtmlToPdfInterceptor ===> Loading page (1/2)
2022-12-13 16:36:59,873 INFO HtmlToPdfInterceptor ===> [>                                                           ] 0%
2022-12-13 16:36:59,873 INFO HtmlToPdfInterceptor ===> [======>                                                     ] 10%
2022-12-13 16:37:02,427 INFO HtmlToPdfInterceptor ===> Error: Failed to load http://d/Source/secp-template-center/secp-template-service/target/classes/templates/thymeleaf/Seeds-Pro/test.html, with network status code 3 and http status code 0 - Host d not found
2022-12-13 16:37:02,428 INFO HtmlToPdfInterceptor ===> Error: Failed loading page http:///D:/Source/secp-template-center/secp-template-service/target/classes/templates/thymeleaf/Seeds-Pro/test.html (sometimes it will work just to ignore this error with --load-error-handling ignore)
2022-12-13 16:37:02,428 INFO HtmlToPdfInterceptor ===> Exit with code 1 due to network error: HostNotFoundError

但是同样的命令,直接在 cmd 里面就可以执行:

wkhtmltopdf 使用本地文件生成 PDF

搜索无果,官方 GitHub 也没找到有用信息,喊同事来看也没发现问题,后来他在自己的 Mac 上拉代码跑了遍,发现正常,而我用的是公司发的 ThinkPad。。。

事后分析,是路径分隔符的问题,wkhtmltopdf 对 Windows 下的路径分隔符兼容性不好,通过 Path.of(first, more) 静态方法结合 File.separator构造路径就没有问题了。

又一个生成失败问题

也是同样的命令,cmd 和 sh 里面都可以执行,服务器上就会报错:

2023-04-10 11:07:47,752 INFO CMD----> wkhtmltoimage --quality 40 "/home/templates/thymeleaf/Seeds/1645260793308119040.html" "/home/templates/thymeleaf/Seeds/1645260793308119040.png"
2023-04-10 11:07:47,783 INFO HtmlToPdfInterceptor ===> Loading page (1/2)
2023-04-10 11:07:47,783 INFO HtmlToPdfInterceptor ===> [>                                                           ] 0%
2023-04-10 11:07:47,784 INFO HtmlToPdfInterceptor ===> [======>                                                     ] 10%
2023-04-10 11:07:47,835 INFO HtmlToPdfInterceptor ===> Error: Failed to load http://home/templates/thymeleaf/Seeds/1645260793308119040.html", with network status code 3 and http status code 0 - Host home not found
2023-04-10 11:07:47,835 INFO HtmlToPdfInterceptor ===> Error: Failed loading page http:/home/templates/thymeleaf/Seeds/1645260793308119040.html" (sometimes it will work just to ignore this error with --load-error-handling ignore)
2023-04-10 11:07:47,835 INFO HtmlToPdfInterceptor ===> Exit with code 1 due to network error: HostNotFoundError

注意看倒数第三行 Failed to load http://home/templates/thymeleaf/Seeds/1645260793308119040.html",路径 /home 前面的双引号没有了,本来怀疑是 wkhtml 的问题,后来根据关键词 Runtime.exec 双引号 检索,发现一篇好文 java getRuntime().exec 带符号的命令 无法执行 解决方法,解决方法不能直接使用命令字符串,而要使用命令字符串数组。

OpenFeign 不支持 HttpServletResponse 问题

模板中心服务返回的 HttpServletResponse,OpenFeign 不支持,只能将文件转为 Base64 返回,另外的方法是使用 RestTemplate 或者 HttpClient

使用方 getOutputStream() has already been called for this response 错误

使用方通过 GET 请求获取 PDF 的时候,返回值必须是 void,因为返回文件使用的是 HttpServletResponse,而 SpringMVC 返回的时候也会使用 response 的 getOutputStream() 方法,而该方法只能调用一次。

posted @ 2022-12-13 21:48  ageovb  阅读(2350)  评论(0编辑  收藏  举报