一文詳解Java中的動態(tài)填充Html模版并轉(zhuǎn)PDF
一、引言
隨著Web開發(fā)的不斷發(fā)展,前端技術(shù)日新月異,后端技術(shù)也在不斷進(jìn)步。在后端技術(shù)中,模板引擎和PDF生成工具是兩個非常重要的領(lǐng)域。Thymeleaf和wkhtmltopdf是這兩個領(lǐng)域的杰出代表。本文將介紹Thymeleaf和wkhtmltopdf的技術(shù)特點(diǎn),并探討它們在Web開發(fā)中的應(yīng)用。
二、Thymeleaf技術(shù)介紹
1. Thymeleaf概述
Thymeleaf是一個Java庫,用于在Web應(yīng)用程序中處理HTML、XML、JavaScript、CSS和文本文件。它是一種聲明式模板引擎,可以在服務(wù)器端生成動態(tài)內(nèi)容。
2. Thymeleaf特點(diǎn)
(1)易于使用:Thymeleaf語法簡單明了,易于學(xué)習(xí)和使用。
(2)支持國際化:Thymeleaf支持多語言環(huán)境,方便實(shí)現(xiàn)國際化。
(3)與Spring框架集成:Thymeleaf與Spring框架無縫集成,方便開發(fā)人員快速構(gòu)建Web應(yīng)用程序。
三、wkhtmltopdf技術(shù)介紹
1. wkhtmltopdf概述
wkhtmltopdf是一個開源工具,用于將HTML頁面轉(zhuǎn)換為PDF文件。它基于WebKit引擎,可以生成高質(zhì)量的PDF文件。
2. wkhtmltopdf特點(diǎn)
(1)高質(zhì)量輸出:wkhtmltopdf可以生成高質(zhì)量的PDF文件,保持原始HTML頁面的布局和樣式。
(2)多種輸出格式:除了PDF格式外,wkhtmltopdf還支持其他輸出格式,如PostScript、EPS等。
(3)命令行工具:wkhtmltopdf提供了命令行工具,方便用戶在終端中直接使用。
四、Thymeleaf與wkhtmltopdf的結(jié)合應(yīng)用
1. 生成動態(tài)PDF文件
使用Thymeleaf模板引擎生成動態(tài)HTML頁面,然后通過wkhtmltopdf工具將動態(tài)HTML頁面轉(zhuǎn)換為PDF文件。這種方式適用于需要生成動態(tài)PDF文件的應(yīng)用場景,如在線文檔、報(bào)告等。
2. 自動化測試報(bào)告生成
在Web應(yīng)用程序的自動化測試中,可以使用Thymeleaf模板引擎生成測試報(bào)告的HTML頁面,然后通過wkhtmltopdf工具將測試報(bào)告的HTML頁面轉(zhuǎn)換為PDF文件。這種方式適用于需要生成自動化測試報(bào)告的應(yīng)用場景。
3. 自定義PDF文件生成
使用Thymeleaf模板引擎定義PDF文件的布局和樣式,然后通過wkhtmltopdf工具將定義的PDF文件輸出為最終的PDF文件。這種方式適用于需要自定義PDF文件生成的應(yīng)用場景,如定制化的合同、發(fā)票等。
五、Spring Boot + maven項(xiàng)目 實(shí)際應(yīng)用
準(zhǔn)備工作
從wkhtmltox官網(wǎng)上下載linux的包

安裝命令rpm -Uvh wkhtmltox-0.12.6-1.centos7.x86_64.rpm 安裝完用wkhtmltopdf -V查看版本驗(yàn)證是否安裝成功

thymeleaf使用
引入pom包
<!-- html模版轉(zhuǎn)化 -->
<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: 是否緩存,開發(fā)模式下設(shè)置為false,避免改了模板還要重啟服務(wù)器,線上設(shè)置為true,可以提高性能。 suffix: .html #encoding: UTF-8 #content-type: text/html mode: HTML
設(shè)置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醫(yī)院電子病歷</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;"> 聯(lián)系電話:<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;">醫(yī)生簽名:<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
/**
* 導(dǎo)出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()));
// 創(chuàng)建一個容器模板
TemplateSpec templateSpec = new TemplateSpec("patMedicalRecord.html", TemplateMode.HTML);
// 設(shè)置模板變量
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);
// 設(shè)置響應(yīng)頭
response.setContentType(ContentType.OCTET_STREAM.getValue());
response.setCharacterEncoding("utf-8");
response.setHeader("Content-Disposition", "attachment; filename="" + file.getName() + """);
// 將文件內(nèi)容寫入響應(yīng)流
try (InputStream inputStream = new FileInputStream(file)) {
IoUtil.copy(inputStream, response.getOutputStream());
} catch (IOException e) {
// 異常處理
log.info("導(dǎo)出電子病歷寫入流失敗,{}", e.getMessage());
}
// 導(dǎo)出完刪除
FileUtil.del(file);
FileUtil.del(templatePath);
}
WKHtmlToPdfUtil 轉(zhuǎn)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轉(zhuǎn)pdf
*
* @param srcPath html路徑,可以是硬盤上的路徑,也可以是網(wǎng)絡(luò)路徑
* @param destPath pdf保存路徑
* @return 轉(zhuǎn)換成功返回true
*/
public static boolean convert(String srcPath, String destPath) {
File file = new File(destPath);
File parent = file.getParentFile();
// 如果pdf保存路徑不存在,則創(chuàng)建路徑
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 系統(tǒng)
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;
}
}
六、總結(jié)與展望
本文介紹了Thymeleaf和wkhtmltopdf的技術(shù)特點(diǎn)和應(yīng)用場景,并探討了它們在Web開發(fā)中的結(jié)合應(yīng)用。隨著Web開發(fā)的不斷發(fā)展,模板引擎和PDF生成工具將會更加普及和重要。未來,我們可以期待更多的技術(shù)進(jìn)步和創(chuàng)新,為Web開發(fā)帶來更多的便利和可能性。
到此這篇關(guān)于一文詳解Java中的動態(tài)填充Html模版并轉(zhuǎn)PDF的文章就介紹到這了,更多相關(guān)Java動態(tài)填充Html模版內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
java實(shí)現(xiàn)簡單的圖書管理系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)簡單的圖書管理系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2021-07-07
Spring Boot中進(jìn)行 文件上傳和 文件下載功能實(shí)現(xiàn)
開發(fā)Wb應(yīng)用時,文件上傳是很常見的一個需求,瀏覽器 通過 表單形式 將 文件 以 流的形式傳遞 給 服務(wù)器,服務(wù)器再對上傳的數(shù)據(jù)解析處理,下面將通過一個案例講解使用 SpringBoot 實(shí)現(xiàn) 文件上傳,感興趣的朋友一起看看吧2024-07-07
java中數(shù)組list map三者之間的互轉(zhuǎn)介紹
java中 數(shù)組 list map之間的互轉(zhuǎn)一張圖清晰呈現(xiàn)并附有代碼,不懂的朋友可以參考下2013-10-10
SSH框架網(wǎng)上商城項(xiàng)目第19戰(zhàn)之訂單信息級聯(lián)入庫以及頁面緩存問題
這篇文章主要介紹了SSH框架網(wǎng)上商城項(xiàng)目第19戰(zhàn)之訂單信息級聯(lián)入庫以及頁面緩存問題,感興趣的小伙伴們可以參考一下2016-06-06
springboot整合RabbitMQ 中的 TTL實(shí)例代碼
TTL 是 RabbitMQ 中一個消息或者隊(duì)列的屬性,表明一條消息或者該隊(duì)列中的所有消息的最大存活時間,單位是毫秒,這篇文章主要介紹了springboot整合RabbitMQ 中的 TTL,需要的朋友可以參考下2022-09-09

