java 将html 转换成pdf 的方法

2025-11-08 06:58:03

1、引子

当我们写一个web程序时,经常会遇到将某个特定页面的内容导出成报告的功能。本文将实现利用Java 8,Spring Boot, Wkhtmltopdf, Thymeleaf将HTML页面导出成PDF文本。

2.总纲

在我们实现具体功能前,我们先看看创建一个PDF文档的过程大约分三步走:

a)     浏览器(服务端)发送一个HTTP请求到我们的后台程序,并说明HTML页面的url和所创建的PDF文档的文件名;

b)     写Java后台代码调用并 wkhtmltopdf 命令行工具以读取HTML文档并将其转为PDF文档;

c)     Java后台程序读取转好的PDF文档,并将其返回到到浏览器端。

在开始前我们之先,先安装wkhtmltopdf

3.技术实现

1.     安装Wkhtmltopdf

首先我们需要安装wkhtmltopdf命令行工具。我们可以去其官网选择对应的操作系统版本下载并安装(本文作者安装的是windows-64bit版本)

官网下载地址:https://wkhtmltopdf.org/downloads.html

如果你用的是 macOS 可以利用Homebrew进行wkhtmltopdf的安装。只要输入如下命令行即可完成安装:

brew install Caskroom/cask/wkhtmltopdf

2.     配置环境变量

我的Wkhtmltopdf是默认安装路径’C:\ProgramFiles\wkhtmltopdf’

计算机-属性-高级系统设置-环境变量-系统变量-Path添加wkhtmltopdf的路径,如下图所示:

配置完Path后我们就可以去写Java代码啦。

开发环境与工具:

a)     Spring Boot 1.4.3 REALEASE

b)     Thymeleaf

c)     Maven 3.3

d)     Eclipse oxygen

3.   项目最终结构

4.    项目依赖 pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

  <modelVersion>4.0.0</modelVersion>

  <groupId>org.thinkingingis</groupId>

  <artifactId>spring-boot-htmltopdf</artifactId>

  <version>0.0.1-SNAPSHOT</version>

  <packaging>jar</packaging>

  <name>spring-boot-htmltopdf</name>

  <url>http://maven.apache.org</url>

  <parent>

        <groupId>org.springframework.boot</groupId>

        <artifactId>spring-boot-starter-parent</artifactId>

        <version>1.4.3.RELEASE</version>

  </parent>

  <properties>

    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

    <java.version>1.8</java.version>

  </properties>

  <dependencies>

        <dependency>

            <groupId>org.springframework.boot</groupId>

            <artifactId>spring-boot-starter-thymeleaf</artifactId>

        </dependency>

        <dependency>

  <groupId>org.springframework.boot</groupId>

  <artifactId>spring-boot-starter-logging</artifactId>

  </dependency>

        

        <dependency>

            <groupId>org.springframework.boot</groupId>

            <artifactId>spring-boot-devtools</artifactId>

            <optional>true</optional>

        </dependency>

        <dependency>  

            <groupId>org.webjars</groupId>  

            <artifactId>bootstrap</artifactId>  

            <version>3.3.7</version>  

        </dependency>  

  </dependencies>

  <build>

        <plugins>

            <plugin>

                <groupId>org.springframework.boot</groupId>

                <artifactId>spring-boot-maven-plugin</artifactId>

            </plugin>

        </plugins>

    </build>

</project>

5.  从HTML页面创建PDF文档

5.1  Model层

在我们具体实现HTML转PDF功能之前,我们需要创建一个类用于存放wkhtmltopdf所需要的参数信息。

我们创建PdfFileRequest .java 类,包含两个属性

  filename 属性是我们所创建PDF文档的文件名

  sourceHtmlUrl 属性是HTML文档的URL地址

PdfFileRequest .java

package org.thinkingingis.model;

public class PdfFileRequest {

private String fileName;

private String sourceHtmlUrl;

public String getFileName() {

return fileName;

}

public void setFileName(String fileName) {

this.fileName = fileName;

}

public String getSourceHtmlUrl() {

return sourceHtmlUrl;

}

public void setSourceHtmlUrl(String sourceHtmlUrl) {

this.sourceHtmlUrl = sourceHtmlUrl;

}

}

5.2  Service层

PdfFileCreator.java

package org.thinkingingis.service;

import java.io.BufferedReader;

import java.io.IOException;

import java.io.InputStream;

import java.io.InputStreamReader;

import java.io.OutputStream;

import java.io.StringWriter;

import java.util.Arrays;

import java.util.List;

import java.util.concurrent.TimeUnit;

import javax.servlet.http.HttpServletResponse;

import org.apache.tomcat.util.http.fileupload.IOUtils;

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

import org.springframework.stereotype.Service;

import org.thinkingingis.model.PdfFileRequest;

@Service

public class PdfFileCreator {

private static final Logger LOGGER = LoggerFactory.getLogger(PdfFileCreator.class);

public void writePdfToResponse(PdfFileRequest fileRequest, HttpServletResponse response) {

        String pdfFileName = fileRequest.getFileName();

        requireNotNull(pdfFileName, "The file name of the created PDF must be set");

        requireNotEmpty(pdfFileName, "File name of the created PDF cannot be empty");

 

        String sourceHtmlUrl = fileRequest.getSourceHtmlUrl();

        requireNotNull(sourceHtmlUrl, "Source HTML url must be set");

        requireNotEmpty(sourceHtmlUrl, "Source HTML url cannot be empty");

 

        List<String> pdfCommand = Arrays.asList(

                "wkhtmltopdf",

                sourceHtmlUrl,

                "-"

        );

 

        ProcessBuilder pb = new ProcessBuilder(pdfCommand);

        Process pdfProcess;

 

        try {

            pdfProcess = pb.start();

 

            try(InputStream in = pdfProcess.getInputStream()) {

                writeCreatedPdfFileToResponse(in, response);

                waitForProcessBeforeContinueCurrentThread(pdfProcess);

                requireSuccessfulExitStatus(pdfProcess);

                setResponseHeaders(response, fileRequest);

            }

            catch (Exception ex) {

                writeErrorMessageToLog(ex, pdfProcess);

                throw new RuntimeException("PDF generation failed");

            }

            finally {

                pdfProcess.destroy();

            }

        }

        catch (IOException ex) {

            throw new RuntimeException("PDF generation failed");

        }

    }

 

    private void requireNotNull(String value, String message) {

        if (value == null) {

            throw new IllegalArgumentException(message);

        }

    }

 

    private void requireNotEmpty(String value, String message) {

        if (value.isEmpty()) {

            throw new IllegalArgumentException(message);

        }

    }

 

    private void writeCreatedPdfFileToResponse(InputStream in, HttpServletResponse response) throws IOException {

        OutputStream out = response.getOutputStream();

        IOUtils.copy(in, out);

        out.flush();

    }

 

    private void waitForProcessBeforeContinueCurrentThread(Process process) {

        try {

            process.waitFor(2, TimeUnit.SECONDS);

        }

        catch (InterruptedException ex) {

            Thread.currentThread().interrupt();

        }

    }

 

    private void requireSuccessfulExitStatus(Process process) {

        if (process.exitValue() != 0) {

            throw new RuntimeException("PDF generation failed");

        }

    }

 

    private void setResponseHeaders(HttpServletResponse response, PdfFileRequest fileRequest) {

        response.setContentType("application/pdf");

        response.setHeader("Content-Disposition", "attachment; filename=\"" + fileRequest.getFileName() + "\"");

    }

 

    private void writeErrorMessageToLog(Exception ex, Process pdfProcess) throws IOException {

        LOGGER.error("Could not create PDF because an exception was thrown: ", ex);

        LOGGER.error("The exit value of PDF process is: {}", pdfProcess.exitValue());

 

        String errorMessage = getErrorMessageFromProcess(pdfProcess);

        LOGGER.error("PDF process ended with error message: {}", errorMessage);

    }

 

    private String getErrorMessageFromProcess(Process pdfProcess) {

        try {

            BufferedReader reader = new BufferedReader(new InputStreamReader(pdfProcess.getErrorStream()));

            StringWriter writer = new StringWriter();

 

            String line;

            while ((line = reader.readLine()) != null) {

                writer.append(line);

            }

 

            return writer.toString();

        }

        catch (IOException ex) {

            LOGGER.error("Could not extract error message from process because an exception was thrown", ex);

            return "";

        }

    }

}

5.3  REST API实现

PdfController.java 

package org.thinkingingis.controller;

import javax.servlet.http.HttpServletResponse;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.web.bind.annotation.RequestBody;

import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.RequestMethod;

import org.springframework.web.bind.annotation.RestController;

import org.thinkingingis.model.PdfFileRequest;

import org.thinkingingis.service.PdfFileCreator;

@RestController

public class PdfController {

private final PdfFileCreator pdfFileCreator;

@Autowired

public PdfController(PdfFileCreator pdfFileCreator) {

this.pdfFileCreator = pdfFileCreator;

}

@RequestMapping(value = "/api/pdf", method = RequestMethod.POST)

public void createPdf(@RequestBody PdfFileRequest fileRequest, HttpServletResponse response) {

pdfFileCreator.writePdfToResponse(fileRequest, response);

}

}

5.4   Controller层

PrintPdfController.java

package org.thinkingingis.controller;

import java.io.ByteArrayInputStream;

import java.io.IOException;

import java.io.InputStream;

import java.io.OutputStream;

import javax.servlet.http.HttpServletResponse;

import org.apache.tomcat.util.http.fileupload.IOUtils;

import org.springframework.boot.web.client.RestTemplateBuilder;

import org.springframework.stereotype.Controller;

import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.RequestMethod;

import org.springframework.web.client.RestTemplate;

import org.thinkingingis.model.PdfFileRequest;

@Controller

@RequestMapping("/print")

public class PrintPdfController {

private final RestTemplate restTemplate;

    public PrintPdfController(RestTemplateBuilder restTemplateBuilder) {

        this.restTemplate = restTemplateBuilder.build();

    }

    

    @RequestMapping(value = "/pdf", method = RequestMethod.GET)

    public void createPdfFromUrl(HttpServletResponse response) {

        PdfFileRequest fileRequest = new PdfFileRequest();

        fileRequest.setFileName("index.pdf");

        fileRequest.setSourceHtmlUrl("http://blog.csdn.net/gisboygogogo/article/");

        byte[] pdfFile = restTemplate.postForObject("http://localhost:8080/api/pdf", 

                fileRequest, 

                byte[].class

        );

        writePdfFileToResponse(pdfFile, "index.pdf", response);

    }

    private void writePdfFileToResponse(byte[] pdfFile, String fileName, HttpServletResponse response) {

        try (InputStream in = new ByteArrayInputStream(pdfFile)) {

            OutputStream out = response.getOutputStream();

            IOUtils.copy(in, out);

            out.flush();

            response.setContentType("application/pdf");

            response.setHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\"");

        }

        catch (IOException ex) {

            throw new RuntimeException("Error occurred when creating PDF file", ex);

        }

    }

}

6. 启动项目

输入 http://localhost:8080/index

点击 ‘打印’ 就会将 ‘http://blog.csdn.net/gisboygogogo/article/’ 页面转成PDF文档,保存即可。

声明:本网站引用、摘录或转载内容仅供网站访问者交流或参考,不代表本站立场,如存在版权或非法内容,请联系站长删除,联系邮箱:site.kefu@qq.com。
猜你喜欢