前言
-
一开始准备使用
xhtmlrenderer
实现html
转pdf
,好处是不用安装,mavn
中直接引入依赖就可以使用。但使用过程中发现它只支持css2.1
,很多样式显示不出来,而且对html
中标签的要求很高,如果标签不闭合则不能使用。 -
wkhtmltopdf
对样式支持很好,基本能还原网页内容,缺点是需要在服务器中安装才能使用。
安装wkhtmltopdf
- 本地环境为
win10
- 生产环境为
Ubuntu 5.4.0-6ubuntu1~16.04.4
windows环境安装
-
首先根据电脑系统下载安装包wkhtmltopdf官网
-
安装后在系统变量
Path
中增加bin目录,我的是D:\Program Files\wkhtmltopdf\bin
-
使用它很简单,在
cmd
中执行命令wkhtmltopdf https://www.baidu.com D://test.pdf
,进度条走完后就会在D盘下生成test.pdf
文件
linux使用docker安装
- 在
DockerFile
文件中增加代码:
RUN apt-get update && \
apt-get install -y xvfb && \
apt-get install -y wkhtmltopdf
# 拷贝字体,否则中文显示不出来
COPY src/main/resources/fonts/msyh.ttf /usr/share/fonts/
COPY src/main/resources/fonts/minijiankai.ttf /usr/share/fonts/
- 一定要将字体拷贝到
/usr/share/fonts/
中,否则生成的pdf
不显示中文
java代码
- 项目需求是前端提供
html
字符串给后台接口,然后转换成pdf
,所以需要先将html
字符串生成html
文件,代码如下:
public void pdf(String html) {
String uuid = UUID.randomUUID().toString();
//生成的html文件名
String htmlFileName = uuid + ".html";
//转换后的pdf文件名
String pdfFileName = uuid + ".pdf";
//根据系统判断文件存放路径
String os = System.getProperty("os.name");
String filePath;
if (os.toLowerCase().indexOf("win") > -1) {
filePath = System.getProperty("java.io.tmpdir") + File.separator + "ReadHtml" + File.separator + htmlFileName;
} else {
filePath = File.separator + "data" + File.separator + "tmp" + File.separator + "ReadHtml" + File.separator + htmlFileName;
}
File htmlFile = null;
File pdfFile = null;
try {
//生成html文件
htmlFile = new File(filePath);
if (!htmlFile.getParentFile().exists()) {
htmlFile.getParentFile().mkdirs();
}
//将html字符串写入到html文件中
FileWriter fileWriter = new FileWriter(htmlFile);
fileWriter.write(html);
fileWriter.close();
//调用wkhtmltopdf将html转为pdf
if (os.toLowerCase().indexOf("win") > -1) {
String cmd = "cmd /c wkhtmltopdf " + htmlFile.getPath() + " " + htmlFile.getParent() + File.separator + pdfFileName;
Process process = Runtime.getRuntime().exec(cmd);
process.waitFor();
} else {
String cmd = "wkhtmltopdf " + htmlFile.getPath() + " " + htmlFile.getParent() + File.separator + pdfFileName;
Process exec = Runtime.getRuntime().exec(cmd);
exec.waitFor();
}
pdfFile = new File(htmlFile.getParent() + File.separator + pdfFileName);
if (pdfFile.exists()) {
System.out.println("成功");
} else {
System.out.println("失败");
}
} catch (Exception e) {
e.printStackTrace();
} finally {
//删除临时文件
// if (htmlFile != null && htmlFile.exists()) {
// htmlFile.delete();
// }
// if (pdfFile != null && pdfFile.exists()) {
// pdfFile.delete();
// }
}
}
- 以上代码在
windows
中可以执行,但是在linux
中不能执行 - 进入
docker
容器,直接执行wkhtmltopdf
命令发现报错如下:
QStandardPaths: XDG_RUNTIME_DIR not set, defaulting to '/tmp/runtime-root'
qt.qpa.screen: QXcbConnection: Could not connect to display
- 参考了这篇文章wkhtmltopdf依赖于X server的解决方案,发现是因为没有启动图形用户界面导致的,只需要在命令前增加
xvfb-run
即可,修改后的代码如下:
//在命令前增加 xvfb-run
String cmd = "xvfb-run wkhtmltopdf " + htmlFile.getPath() + " " + htmlFile.getParent() + File.separator + pdfFileName;
Process exec = Runtime.getRuntime().exec(cmd);
exec.waitFor();