一文詳解Java中的動(dòng)態(tài)填充Html模版并轉(zhuǎn)PDF
一、引言
隨著Web開(kāi)發(fā)的不斷發(fā)展,前端技術(shù)日新月異,后端技術(shù)也在不斷進(jìn)步。在后端技術(shù)中,模板引擎和PDF生成工具是兩個(gè)非常重要的領(lǐng)域。Thymeleaf和wkhtmltopdf是這兩個(gè)領(lǐng)域的杰出代表。本文將介紹Thymeleaf和wkhtmltopdf的技術(shù)特點(diǎn),并探討它們?cè)赪eb開(kāi)發(fā)中的應(yīng)用。
二、Thymeleaf技術(shù)介紹
1. Thymeleaf概述
Thymeleaf是一個(gè)Java庫(kù),用于在Web應(yīng)用程序中處理HTML、XML、JavaScript、CSS和文本文件。它是一種聲明式模板引擎,可以在服務(wù)器端生成動(dòng)態(tài)內(nèi)容。
2. Thymeleaf特點(diǎn)
(1)易于使用:Thymeleaf語(yǔ)法簡(jiǎn)單明了,易于學(xué)習(xí)和使用。
(2)支持國(guó)際化:Thymeleaf支持多語(yǔ)言環(huán)境,方便實(shí)現(xiàn)國(guó)際化。
(3)與Spring框架集成:Thymeleaf與Spring框架無(wú)縫集成,方便開(kāi)發(fā)人員快速構(gòu)建Web應(yīng)用程序。
三、wkhtmltopdf技術(shù)介紹
1. wkhtmltopdf概述
wkhtmltopdf是一個(gè)開(kāi)源工具,用于將HTML頁(yè)面轉(zhuǎn)換為PDF文件。它基于WebKit引擎,可以生成高質(zhì)量的PDF文件。
2. wkhtmltopdf特點(diǎn)
(1)高質(zhì)量輸出:wkhtmltopdf可以生成高質(zhì)量的PDF文件,保持原始HTML頁(yè)面的布局和樣式。
(2)多種輸出格式:除了PDF格式外,wkhtmltopdf還支持其他輸出格式,如PostScript、EPS等。
(3)命令行工具:wkhtmltopdf提供了命令行工具,方便用戶(hù)在終端中直接使用。
四、Thymeleaf與wkhtmltopdf的結(jié)合應(yīng)用
1. 生成動(dòng)態(tài)PDF文件
使用Thymeleaf模板引擎生成動(dòng)態(tài)HTML頁(yè)面,然后通過(guò)wkhtmltopdf工具將動(dòng)態(tài)HTML頁(yè)面轉(zhuǎn)換為PDF文件。這種方式適用于需要生成動(dòng)態(tài)PDF文件的應(yīng)用場(chǎng)景,如在線文檔、報(bào)告等。
2. 自動(dòng)化測(cè)試報(bào)告生成
在Web應(yīng)用程序的自動(dòng)化測(cè)試中,可以使用Thymeleaf模板引擎生成測(cè)試報(bào)告的HTML頁(yè)面,然后通過(guò)wkhtmltopdf工具將測(cè)試報(bào)告的HTML頁(yè)面轉(zhuǎn)換為PDF文件。這種方式適用于需要生成自動(dòng)化測(cè)試報(bào)告的應(yīng)用場(chǎng)景。
3. 自定義PDF文件生成
使用Thymeleaf模板引擎定義PDF文件的布局和樣式,然后通過(guò)wkhtmltopdf工具將定義的PDF文件輸出為最終的PDF文件。這種方式適用于需要自定義PDF文件生成的應(yīng)用場(chǎ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: 是否緩存,開(kāi)發(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;"> 病案號(hào):<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)建一個(gè)容器模板 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)容寫(xiě)入響應(yīng)流 try (InputStream inputStream = new FileInputStream(file)) { IoUtil.copy(inputStream, response.getOutputStream()); } catch (IOException e) { // 異常處理 log.info("導(dǎo)出電子病歷寫(xiě)入流失敗,{}", e.getMessage()); } // 導(dǎo)出完刪除 FileUtil.del(file); FileUtil.del(templatePath); }
WKHtmlToPdfUtil 轉(zhuǎn)pdf工具類(lèi)
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路徑,可以是硬盤(pán)上的路徑,也可以是網(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)用場(chǎng)景,并探討了它們?cè)赪eb開(kāi)發(fā)中的結(jié)合應(yīng)用。隨著Web開(kāi)發(fā)的不斷發(fā)展,模板引擎和PDF生成工具將會(huì)更加普及和重要。未來(lái),我們可以期待更多的技術(shù)進(jìn)步和創(chuàng)新,為Web開(kāi)發(fā)帶來(lái)更多的便利和可能性。
到此這篇關(guān)于一文詳解Java中的動(dòng)態(tài)填充Html模版并轉(zhuǎn)PDF的文章就介紹到這了,更多相關(guān)Java動(dòng)態(tài)填充Html模版內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
java實(shí)現(xiàn)簡(jiǎn)單的圖書(shū)管理系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)簡(jiǎn)單的圖書(shū)管理系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-07-07Spring Boot中進(jìn)行 文件上傳和 文件下載功能實(shí)現(xiàn)
開(kāi)發(fā)Wb應(yīng)用時(shí),文件上傳是很常見(jiàn)的一個(gè)需求,瀏覽器 通過(guò) 表單形式 將 文件 以 流的形式傳遞 給 服務(wù)器,服務(wù)器再對(duì)上傳的數(shù)據(jù)解析處理,下面將通過(guò)一個(gè)案例講解使用 SpringBoot 實(shí)現(xiàn) 文件上傳,感興趣的朋友一起看看吧2024-07-07java中數(shù)組list map三者之間的互轉(zhuǎn)介紹
java中 數(shù)組 list map之間的互轉(zhuǎn)一張圖清晰呈現(xiàn)并附有代碼,不懂的朋友可以參考下2013-10-10SSH框架網(wǎng)上商城項(xiàng)目第19戰(zhàn)之訂單信息級(jí)聯(lián)入庫(kù)以及頁(yè)面緩存問(wèn)題
這篇文章主要介紹了SSH框架網(wǎng)上商城項(xiàng)目第19戰(zhàn)之訂單信息級(jí)聯(lián)入庫(kù)以及頁(yè)面緩存問(wèn)題,感興趣的小伙伴們可以參考一下2016-06-06springboot整合RabbitMQ 中的 TTL實(shí)例代碼
TTL 是 RabbitMQ 中一個(gè)消息或者隊(duì)列的屬性,表明一條消息或者該隊(duì)列中的所有消息的最大存活時(shí)間,單位是毫秒,這篇文章主要介紹了springboot整合RabbitMQ 中的 TTL,需要的朋友可以參考下2022-09-09springboot如何讀取配置文件到靜態(tài)工具類(lèi)
這篇文章主要介紹了springboot實(shí)現(xiàn)讀取配置文件到靜態(tài)工具類(lèi)方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-12-12