一文详解Java中的动态填充Html模版并转PDF
作者:不知名的码字员
一、引言
随着Web开发的不断发展,前端技术日新月异,后端技术也在不断进步。在后端技术中,模板引擎和PDF生成工具是两个非常重要的领域。Thymeleaf和wkhtmltopdf是这两个领域的杰出代表。本文将介绍Thymeleaf和wkhtmltopdf的技术特点,并探讨它们在Web开发中的应用。
二、Thymeleaf技术介绍
1. Thymeleaf概述
Thymeleaf是一个Java库,用于在Web应用程序中处理HTML、XML、JavaScript、CSS和文本文件。它是一种声明式模板引擎,可以在服务器端生成动态内容。
2. Thymeleaf特点
(1)易于使用:Thymeleaf语法简单明了,易于学习和使用。
(2)支持国际化:Thymeleaf支持多语言环境,方便实现国际化。
(3)与Spring框架集成:Thymeleaf与Spring框架无缝集成,方便开发人员快速构建Web应用程序。
三、wkhtmltopdf技术介绍
1. wkhtmltopdf概述
wkhtmltopdf是一个开源工具,用于将HTML页面转换为PDF文件。它基于WebKit引擎,可以生成高质量的PDF文件。
2. wkhtmltopdf特点
(1)高质量输出:wkhtmltopdf可以生成高质量的PDF文件,保持原始HTML页面的布局和样式。
(2)多种输出格式:除了PDF格式外,wkhtmltopdf还支持其他输出格式,如PostScript、EPS等。
(3)命令行工具:wkhtmltopdf提供了命令行工具,方便用户在终端中直接使用。
四、Thymeleaf与wkhtmltopdf的结合应用
1. 生成动态PDF文件
使用Thymeleaf模板引擎生成动态HTML页面,然后通过wkhtmltopdf工具将动态HTML页面转换为PDF文件。这种方式适用于需要生成动态PDF文件的应用场景,如在线文档、报告等。
2. 自动化测试报告生成
在Web应用程序的自动化测试中,可以使用Thymeleaf模板引擎生成测试报告的HTML页面,然后通过wkhtmltopdf工具将测试报告的HTML页面转换为PDF文件。这种方式适用于需要生成自动化测试报告的应用场景。
3. 自定义PDF文件生成
使用Thymeleaf模板引擎定义PDF文件的布局和样式,然后通过wkhtmltopdf工具将定义的PDF文件输出为最终的PDF文件。这种方式适用于需要自定义PDF文件生成的应用场景,如定制化的合同、发票等。
五、Spring Boot + maven项目 实际应用
准备工作
从wkhtmltox官网上下载linux的包
安装命令rpm -Uvh wkhtmltox-0.12.6-1.centos7.x86_64.rpm
安装完用wkhtmltopdf -V
查看版本验证是否安装成功
thymeleaf使用
引入pom包
<!-- html模版转化 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> <dependency> <groupId>org.thymeleaf.extras</groupId> <artifactId>thymeleaf-extras-java8time</artifactId> <version>3.0.4.RELEASE</version> </dependency>
application.yml 配置模版位置
# thymeleaf spring: thymeleaf: prefix: classpath:/templates/ #prefix:指定模板所在的目录 check-template-location: true #check-tempate-location: 检查模板路径是否存在 cache: false #cache: 是否缓存,开发模式下设置为false,避免改了模板还要重启服务器,线上设置为true,可以提高性能。 suffix: .html #encoding: UTF-8 #content-type: text/html mode: HTML
设置html模版
<html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div class="app-container"> <div class="con"> <div class="title">xxx医院电子病历</div> <div></div> <div class="el-row" style="margin-left: -5px; margin-right: -5px;"> <div class="el-col el-col-4 is-guttered" style="padding-right: 5px; padding-left: 5px;"> 病案号:<span th:text="${dto.getRecordNo()}"></span></div> <div class="el-col el-col-2 is-guttered" style="padding-right: 5px; padding-left: 5px;"> 科室:<span th:text="${dto.getDeptName()}"></span></div> <div class="el-col el-col-3 is-guttered" style="padding-right: 5px; padding-left: 5px;"> 就诊日期:<span th:text="${#dates.format(dto.getCreateTime(), 'yyyy-MM-dd')}"></span></div> </div><!----> <div class="box"> <div class="el-row" style="margin-left: -5px; margin-right: -5px;"> <div class="el-col el-col-4 is-guttered" style="padding-right: 5px; padding-left: 5px;"> 姓名:<span th:text="${dto.getCusPatient().getRealName()}"></span></div> <div class="el-col el-col-2 is-guttered" style="padding-right: 5px; padding-left: 5px;"> 性别:<span th:text="${dto.getCusPatient().getGender() == 1 ? '男' : '女'}"></span></div> <div class="el-col el-col-3 is-guttered" style="padding-right: 5px; padding-left: 5px;"> 年龄:<span th:text="${dto.getPatientAge()}"></span></div> </div> <div class="el-row" style="margin-left: -5px; margin-right: -5px;"> <div class="el-col el-col-12 is-guttered" style="padding-right: 5px; padding-left: 5px;"> 身份证:<span th:text="${dto.getCusPatient().getIdNumber()}"></span></div> <div class="el-col el-col-12 is-guttered" style="padding-right: 5px; padding-left: 5px;"> 联系电话:<span th:text="${dto.getCusPatient().getPhoneNumber()}"></span></div> </div> <div class="box-bottom"> <div>主诉:<span th:text="${dto.getChiefComplaint()}"></span></div> <div>诊断:<span th:text="${dto.getDiseaseIcdText()}"></span></div> <div>病史描述:</div> <div><span th:text="${dto.getDiseaseDesc()}"></span></div> <div>处理:<span th:text="${dto.getTreatment()}"></span></div> <div style="overflow: hidden"> <div style="float: right;">医生签名:<span th:text="${dto.getDoctorName()}"></span></div> </div> </div> </div> </div> </div> </body> <style> * { margin: 0; padding: 0; font-size: 16px; color: #333333; } .app-container .con { box-sizing: border-box; width: 100%; line-height: 23px; font-size: 14px; color: #333; } .title { text-align: center; height: 31px; font-size: 22px; font-weight: 600; color: #333333; line-height: 26px; margin-bottom: 10px; } .box { border: 1px solid #999; } .box .el-row { border-bottom: 1px solid #888; padding: 10px; } .box .el-row .el-col { height: 22px; } .con > .el-row { margin-bottom: 10px; } .el-row { box-sizing: border-box; overflow: hidden; margin-left: 0!important; margin-right: 0!important; } .el-row .el-col { float: left; width: 33.33%; padding-left: 0 !important; padding-right: 0 !important; } .el-row .el-col-12 { width: 50%; } .box-bottom { padding: 10px; } .box-bottom > div { margin-bottom: 6px; line-height: 1.5; } </style> </html>
定义controller
/** * 导出pdf * * @param response 流 * @param medicalRecordId 病历id */ @GetMapping("/patient/patMedicalRecord/export/{medicalRecordId}") public void exportPatMedicalRecord(HttpServletResponse response, @PathVariable Long medicalRecordId) { PatMedicalRecordDetailDTO dto = patMedicalRecordFacade.getByMedicalRecordId(medicalRecordId); dto.setCusPatient(SystemCacheUtils.getPatientById(dto.getPatientId())); // 创建一个容器模板 TemplateSpec templateSpec = new TemplateSpec("patMedicalRecord.html", TemplateMode.HTML); // 设置模板变量 Context context = new Context(); context.setVariable("dto", dto); // 渲染并返回新的HTML文本 String newHtml = templateEngine.process(templateSpec, context); String fileName = StrUtil.format("{}patMedicalRecord_tmp_{}", tcmTemplateConfigProperties.getPath(), new Date().getTime()); String templatePath = fileName + ".html"; String pdfPath = fileName + ".pdf"; FileUtil.writeString(newHtml,templatePath, "UTF-8"); WKHtmlToPdfUtil.convert(templatePath, pdfPath); File file = FileUtil.file(pdfPath); // 设置响应头 response.setContentType(ContentType.OCTET_STREAM.getValue()); response.setCharacterEncoding("utf-8"); response.setHeader("Content-Disposition", "attachment; filename="" + file.getName() + """); // 将文件内容写入响应流 try (InputStream inputStream = new FileInputStream(file)) { IoUtil.copy(inputStream, response.getOutputStream()); } catch (IOException e) { // 异常处理 log.info("导出电子病历写入流失败,{}", e.getMessage()); } // 导出完删除 FileUtil.del(file); FileUtil.del(templatePath); }
WKHtmlToPdfUtil 转pdf工具类
package com.hkt.tcm.common.util.pdf; import com.hkt.tcm.common.def.consts.Tools; import lombok.extern.slf4j.Slf4j; import java.io.File; /** * @author lixin * @date 2023-12-22 15:52 **/ @Slf4j public class WKHtmlToPdfUtil { /** * html转pdf * * @param srcPath html路径,可以是硬盘上的路径,也可以是网络路径 * @param destPath pdf保存路径 * @return 转换成功返回true */ public static boolean convert(String srcPath, String destPath) { File file = new File(destPath); File parent = file.getParentFile(); // 如果pdf保存路径不存在,则创建路径 if (!parent.exists()) { parent.mkdirs(); } StringBuilder cmd = new StringBuilder(); // wkhtmltopdf的路径 if (System.getProperty("os.name").contains("Mac")) { cmd.append(Tools.WkHtmlToPdf.PATH_MAC); } else if(System.getProperty("os.name").contains("Windows")) { cmd.append(Tools.WkHtmlToPdf.PATH_WINDOWS); } else { cmd.append(Tools.WkHtmlToPdf.PATH_LINUX); } cmd.append(" -L 5mm -R 5mm"); cmd.append(" --no-stop-slow-scripts --load-error-handling ignore"); cmd.append(" --enable-local-file-access"); // cmd.append(StrUtil.format(" --header-right {} --header-line --header-spacing 3", "")); // cmd.append(StrUtil.format(" --header-right {} --header-spacing 3", "")); cmd.append(" "); cmd.append("--enable-local-file-access"); cmd.append(" "); cmd.append("--disable-smart-shrinking "); cmd.append(" ""); cmd.append(srcPath); cmd.append("" "); cmd.append(" "); cmd.append(destPath); System.out.println(cmd.toString()); boolean result = true; Process proc; try { if (!System.getProperty("os.name").contains("Windows")) { // 非windows 系统 proc = Runtime.getRuntime().exec(new String[]{"/bin/sh", "-c", cmd.toString()}); } else { proc = Runtime.getRuntime().exec(cmd.toString()); } HtmlToPdfInterceptor error = new HtmlToPdfInterceptor(proc.getErrorStream()); HtmlToPdfInterceptor output = new HtmlToPdfInterceptor(proc.getInputStream()); error.start(); output.start(); proc.waitFor(); } catch (Exception e) { log.error("Convert to pdf error", e); result = false; } return result; } }
六、总结与展望
本文介绍了Thymeleaf和wkhtmltopdf的技术特点和应用场景,并探讨了它们在Web开发中的结合应用。随着Web开发的不断发展,模板引擎和PDF生成工具将会更加普及和重要。未来,我们可以期待更多的技术进步和创新,为Web开发带来更多的便利和可能性。
到此这篇关于一文详解Java中的动态填充Html模版并转PDF的文章就介绍到这了,更多相关Java动态填充Html模版内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!