你的浏览器不支持canvas

做你害怕做的事情,然后你会发现,不过如此。

Java使用wkhtmltopdf实现html转pdf(windows和linux docker两种环境)

时间: 作者: 黄运鑫

本文章属原创文章,未经作者许可,禁止转载,复制,下载,以及用作商业用途。原作者保留所有解释权。


前言

  • 一开始准备使用xhtmlrenderer实现htmlpdf,好处是不用安装,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 
//在命令前增加 xvfb-run
String cmd = "xvfb-run wkhtmltopdf " + htmlFile.getPath() + " " + htmlFile.getParent() + File.separator + pdfFileName;
Process exec = Runtime.getRuntime().exec(cmd);
exec.waitFor();


对于本文内容有问题或建议的小伙伴,欢迎在文章底部留言交流讨论。