功能实现——使用 OpenPDF 将 HTML 转换为 PDF,并将其上传到 FTP 服务器

avatar
作者
猴君
阅读量:0

目录

1.需求分析

使用 OpenPDF 将 HTML 文件转换为 PDF,并将其上传到 FTP 服务器。

2.项目环境搭建

(1)在 IDEA 中创建一个 Spring Boot 项目,具体可以参考【环境搭建】使用IDEA创建SpringBoot项目详细步骤这篇文章。

(2)pom.xml 中添加如下依赖:

<dependency>     <groupId>org.springframework.boot</groupId>     <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency>  <dependency>     <groupId>org.jsoup</groupId>     <artifactId>jsoup</artifactId>     <version>1.16.2</version> </dependency>  <dependency>     <groupId>com.github.librepdf</groupId>     <artifactId>openpdf</artifactId>     <version>1.3.32</version> </dependency>  <dependency>     <groupId>org.xhtmlrenderer</groupId>     <artifactId>flying-saucer-pdf-openpdf</artifactId>     <version>9.3.1</version> </dependency>  <dependency>     <groupId>org.projectlombok</groupId>     <artifactId>lombok</artifactId>     <version>1.18.30</version> </dependency>  <dependency>     <groupId>commons-net</groupId>     <artifactId>commons-net</artifactId>     <version>3.6</version> </dependency> 

3.将 HTML 转换为 PDF

3.1.代码实现

mail.html

邮件模板 mail.html 如下:

<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head>     <meta charset="UTF-8">     <title>入职欢迎邮件</title>      <style>         body {             font-family: SimHei;         }     </style>  </head> <body>   欢迎 <span th:text="${name}"></span> 加入 XXXX 大家庭,您的入职信息如下:   <table border="1">       <tr>           <td>姓名</td>           <td th:text="${name}"></td>       </tr>       <tr>           <td>职位</td>           <td th:text="${posName}"></td>       </tr>       <tr>           <td>职称</td>           <td th:text="${jobLevelName}"></td>       </tr>       <tr>           <td>部门</td>           <td th:text="${departmentName}"></td>       </tr>   </table>   <p>我们公司的工作忠旨是严格,创新,诚信,您的加入将为我们带来新鲜的血液,带来创新的思维,     以及为我们树立良好的公司形象!希望在以后的工作中我们能够齐心协力,与时俱进,团结协作!     同时也祝您在本公司,工作愉快,实现自己的人生价值!希望在未来的日子里,携手共进!</p> </body> </html> 

HtmlToPDFController.java

package com.example.htmltopdf.controller;  import com.example.htmltopdf.service.PDFConverterService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController;  @RestController @RequestMapping("/htp") public class HtmlToPDFController {      @Autowired     private PDFConverterService pdfConverterService;      @PostMapping("/converter")     public String htmlToPDF() {         pdfConverterService.convertHtmlToPDF();         return "success";     }  } 

PDFConverterService.java

package com.example.htmltopdf.service;  public interface PDFConverterService {      void convertHtmlToPDF();  } 

PDFConverterServiceImpl.java

package com.example.htmltopdf.service.impl;  import com.example.htmltopdf.service.PDFConverterService; import com.lowagie.text.pdf.BaseFont; import lombok.extern.slf4j.Slf4j; import org.jsoup.Jsoup; import org.jsoup.nodes.Document; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.thymeleaf.TemplateEngine; import org.thymeleaf.context.Context; import org.xhtmlrenderer.pdf.ITextRenderer;  import java.io.*; import java.util.HashMap; import java.util.Map;  @Slf4j @Service public class PDFConverterServiceImpl implements PDFConverterService {      @Autowired     private TemplateEngine templateEngine;      @Override     public void convertHtmlToPDF() {         Context context = new Context();         context.setVariables(assembleParameters());         System.out.println("Processing template...");         String htmlContent = templateEngine.process("mail", context);                  Document doc = Jsoup.parse(htmlContent, "utf-8");         //默认是以 html 的方式         doc.outputSettings().syntax(Document.OutputSettings.Syntax.xml);         //需改用 xml 的方式格式,否则用 openPdf 转化时 PDF 时可能排版错乱         byte[] pdfBytes = html2PDF(doc.outerHtml());          //文件保存本地路径         String filePath = "output.pdf";         try (FileOutputStream fos = new FileOutputStream(filePath)) {             //将 byte[] 数据写入文件             fos.write(pdfBytes);             log.info("PDF 文件保存成功:{}", filePath);         } catch (IOException e) {             log.info("保存 PDF 文件时出现错误:{}", e.getMessage());             e.printStackTrace();         }     }      public Map<String, Object> assembleParameters() {         HashMap<String, Object> map = new HashMap<>();         map.put("name", "Tom");         map.put("posName", "software");         map.put("jobLevelName", "高级");         map.put("departmentName", "软件部");         return map;     }      public byte[] html2PDF(String html) {         try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {             ITextRenderer renderer=new ITextRenderer();              // 加载字体文件(SimHei为示例,请根据实际字体文件替换路径)             String fontFile = "/static/fonts/SimHei.ttf";             renderer.getFontResolver().addFont(fontFile, BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);              renderer.setDocumentFromString(html);             renderer.layout();             renderer.createPDF(outputStream);             return outputStream.toByteArray();         } catch (Exception e) {             e.printStackTrace();         }         return new byte[0];     }  } 

3.2.测试

启动项目后,在 Postman 中进行接口测试(注意是 POST 请求):

http://localhost:8080/htp/converter 

运行成功后会发现已经生成了如下 PDF 文件:

在这里插入图片描述

PDF 中的内容如下:

在这里插入图片描述

3.3.注意事项

在 HTML 转为 PDF 时,如果页面存在中文字符,可能会出现转换后中文字符不显示的情况!本文的解决办法如下:

(1)在 HTML 页面中设置字体系列(下面以黑体 SimHei 为例):

<style>     body {         font-family: SimHei;     } </style> 

(2)使用 ITextRenderer 转换时加载对应字体(具体见 html2PDF 方法):

// 加载字体文件(SimHei为示例,请根据实际字体文件替换路径) String fontFile = "/static/fonts/SimHei.ttf"; renderer.getFontResolver().addFont(fontFile, BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED); 

4.将生成的 PDF 上传到 FTP 服务器

相关链接:
Java实现文件上传到ftp服务器
Java从ftp服务器上传与下载文件

4.1.搭建 FTP 服务器

下面所使用的 TFP 服务器搭建在 CentOS 7 上,具体搭建过程可见 Linux - 搭建 FTP 服务器这篇文章。

4.2.配置文件

application.yml 中的内容如下:

server:   port: 8080  ftp:   server:     host: 192.168.101.65     port: 21     username: sc     password: 123     remoteDir: /home/sc 

4.3.代码实现

FtpUtil.java

package com.example.htmltopdf.utils;  import lombok.extern.slf4j.Slf4j; import org.apache.commons.net.ftp.FTP; import org.apache.commons.net.ftp.FTPClient; import org.apache.commons.net.ftp.FTPReply;  import java.io.*; import java.nio.charset.StandardCharsets;  @Slf4j public class FTPUtil {      /**      * 获取一个 FTP 连接      *      * @param host     ip 地址      * @param port     端口      * @param username 用户名      * @param password 密码      * @return 返回 FTP 连接对象      * @throws Exception 连接 FTP 时发生的各种异常      */     public static FTPClient getFtpClient(String host, Integer port, String username, String password) throws Exception {         FTPClient ftpClient = new FTPClient();          //连接服务器         ftpClient.connect(host, port);          int reply = ftpClient.getReplyCode();         if (!FTPReply.isPositiveCompletion(reply)) {             log.error("无法连接至 ftp 服务器,host:{},port:{}", host, port);             ftpClient.disconnect();             return null;         }          //登入服务器         boolean login = ftpClient.login(username, password);         if (!login) {             log.error("登录失败, 用户名或密码错误");             ftpClient.logout();             ftpClient.disconnect();             return null;         }          //连接并且成功登陆 FTP 服务器         log.info("login success ftp server, host: {}, port: {}, user: {}", host, port, username);          //设置通道字符集, 要与服务端设置一致         ftpClient.setControlEncoding("UTF-8");         //设置文件传输编码类型, 字节传输:BINARY_FILE_TYPE, 文本传输:ASCII_FILE_TYPE, 建议使用BINARY_FILE_TYPE进行文件传输         ftpClient.setFileType(FTP.BINARY_FILE_TYPE);         //主动模式: enterLocalActiveMode(),被动模式: enterLocalPassiveMode(),一般选择被动模式         ftpClient.enterLocalPassiveMode();         //切换目录         //ftpClient.changeWorkingDirectory("xxxx");          return ftpClient;     }      /**      * 断开 FTP 连接      *      * @param ftpClient FTP 连接客户端      */     public static void disConnect(FTPClient ftpClient) {         if (ftpClient == null) {             return;         }         try {             log.info("断开 FTP 连接,host: {},port: {}", ftpClient.getPassiveHost(), ftpClient.getPassivePort());             ftpClient.logout();             ftpClient.disconnect();         } catch (IOException e) {             e.printStackTrace();             log.error("FTP 连接断开异常,请检查!");         }      }      /**      * 文件下载      *      * @param ftpClient FTP 连接客户端      * @param path      文件路径      * @param downPath  文件名称      */     public static void download(FTPClient ftpClient, String path, String downPath) throws Exception {         if (ftpClient == null || path == null || downPath == null) {             return;         }          //中文目录处理存在问题, 转化为 FTP 能够识别中文的字符集         String remotePath;         try {             remotePath = new String(path.getBytes(StandardCharsets.UTF_8), FTP.DEFAULT_CONTROL_ENCODING);         } catch (UnsupportedEncodingException e) {             remotePath = path;         }          InputStream inputStream = ftpClient.retrieveFileStream(remotePath);         if (inputStream == null) {             log.error("{} 在 TFP 服务器中不存在,请检查", path);             return;         }          FileOutputStream outputStream = new FileOutputStream(downPath);         BufferedInputStream bufferedInputStream = new BufferedInputStream(inputStream);         BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(outputStream);         try {             byte[] buffer = new byte[2048];             int i;             while ((i = bufferedInputStream.read(buffer)) != -1) {                 bufferedOutputStream.write(buffer, 0, i);                 bufferedOutputStream.flush();             }         } catch (Exception e) {             log.error("文件下载异常", e);             log.error("{} 下载异常,请检查!", path);         }          inputStream.close();         outputStream.close();         bufferedInputStream.close();         bufferedOutputStream.close();          //关闭流之后必须执行,否则下一个文件导致流为空         boolean complete = ftpClient.completePendingCommand();         if (complete) {             log.info("文件 {} 下载完成", remotePath);         } else {             log.error("文件 {} 下载失败", remotePath);         }     }       /**      * 上传文件      *      * @param ftpClient  FTP 连接客户端      * @param sourcePath 源地址      */     public static void upload(FTPClient ftpClient, String sourcePath, String remoteDir) throws Exception {         if (ftpClient == null || sourcePath == null) {             return;         }          File file = new File(sourcePath);         if (!file.exists() || !file.isFile()) {             return;         }          //中文目录处理存在问题,转化为 TFP 能够识别中文的字符集         String remotePath = new String((remoteDir + "/" + file.getName())                     .getBytes(StandardCharsets.UTF_8), FTP.DEFAULT_CONTROL_ENCODING);         try (                 InputStream inputStream = new FileInputStream(file);                 OutputStream outputStream = ftpClient.storeFileStream(remotePath);         ) {             byte[] buffer = new byte[2048];             int length;             while ((length = inputStream.read(buffer)) != -1) {                 outputStream.write(buffer, 0, length);                 outputStream.flush();             }         } catch (Exception e) {             log.error("文件上传异常", e);         }          // 关闭流之后必须执行,否则下一个文件导致流为空         boolean complete = ftpClient.completePendingCommand();         if (complete) {             log.info("文件 {} 上传完成", remotePath);         } else {             log.error("文件 {} 上传失败", remotePath);         }     } } 

FTPController.java

package com.example.htmltopdf.controller;  import com.example.htmltopdf.utils.FTPUtil; import org.apache.commons.net.ftp.FTPClient; import org.apache.commons.net.ftp.FTPFile; import org.springframework.beans.factory.annotation.Value; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController;  @RestController @RequestMapping("/ftp") public class FTPController {      @Value("${ftp.server.host}")     private String FTP_HOST;      @Value("${ftp.server.port}")     private int FTP_PORT;      @Value("${ftp.server.username}")     private String FTP_USERNAME;      @Value("${ftp.server.password}")     private String FTP_PASSWORD;      @Value("${ftp.server.remoteDir}")     private String FTP_REMOTE_DIR;      @PostMapping("/upload")     public String uploadFile() throws Exception {         System.out.println();         FTPClient ftpClient = FTPUtil.getFtpClient(FTP_HOST, FTP_PORT, FTP_USERNAME, FTP_PASSWORD);          // 展示文件夹         assert ftpClient != null;         FTPFile[] ftpFiles = ftpClient.listDirectories();         for (FTPFile file : ftpFiles) {             System.out.println(file.getName());         }          //上传文件         FTPUtil.upload(ftpClient, "output.pdf", FTP_REMOTE_DIR);          //下载文件         FTPUtil.download(ftpClient, FTP_REMOTE_DIR + "/output.pdf", "E:\\output.pdf");          FTPUtil.disConnect(ftpClient);          return "success";     }  } 

4.4.测试

启动项目后,在 Postman 中进行接口测试(注意是 POST 请求):

http://localhost:8080/ftp/upload 

运行成功后会发现 FTP 服务器对应目录中已经有了该 PDF 文件:

在这里插入图片描述

并且下载功能也是正常的,在本地的 E 盘中也出现该 PDF 文件:

在这里插入图片描述

广告一刻

为您即时展示最新活动产品广告消息,让您随时掌握产品活动新动态!