Java調(diào)用Vue前端頁面生成PDF文件的完整代碼
一、業(yè)務(wù)背景
在實(shí)際業(yè)務(wù)使用中,有些情況下打印功能是由后端組裝數(shù)據(jù),前端整理數(shù)據(jù)生成pdf文件。此時如果某個功能需要后端再拿到生成的pdf進(jìn)行下一步處理就不好實(shí)現(xiàn)。
比如后端需要拿到pdf文件發(fā)給CA廠商進(jìn)行鑒權(quán),如果在之前基礎(chǔ)上增加新的交互邏輯整個流程就變得復(fù)雜冗余,于是我們考慮后端直接調(diào)用前端獲取pdf文件。
二、流程概覽
頁面點(diǎn)擊觸發(fā)——>后端組裝參數(shù)調(diào)用Vue生成PDF——>前端收到請求解析參數(shù)繪制PDF——>后端拿到PDF文件進(jìn)行后續(xù)業(yè)務(wù)處理(CA鑒權(quán)等)
其中關(guān)鍵的一點(diǎn)在于后端訪問后需要等待多久再生成PDF,如果固定某個時間間隔可能會出現(xiàn)PDF沒生成全或者已經(jīng)生成完成但是還在等待浪費(fèi)時間。
所以約定了一個屬性,當(dāng)前端頁面渲染完成給這個屬性打標(biāo)記,后端讀取標(biāo)記成功后生成PDF。
三、環(huán)境準(zhǔn)備
該功能需要依賴瀏覽器,我們以谷歌為例,確認(rèn)好本地谷歌瀏覽器版本,下載對應(yīng)的瀏覽器驅(qū)動。(驅(qū)動下載地址:Chrome for Testing availability)


大版本必須一致(139),然后將下載好的驅(qū)動放到固定位置,谷歌瀏覽器exe位置也需要摘出來,稍后需要在Java代碼中配置。
四、后端代碼
需要將驅(qū)動路徑、瀏覽器路徑,前端訪問路徑進(jìn)行配置,前端訪問路由可自行調(diào)整。以下是具體代碼,整體流程簡單需要調(diào)試的配置較多。
private String dllUrl;//存儲驅(qū)動的根目錄,完整路徑代碼拼接,可自行調(diào)整
private String mWebAddress;//存儲前端訪問頁面,后續(xù)訪問路由代碼拼接,可自行調(diào)整
private String chromePath;//瀏覽器啟動程序完整路徑,可自行調(diào)整
package com.aikes.util;
import lombok.extern.slf4j.Slf4j;
import org.openqa.selenium.By;
import org.openqa.selenium.Pdf;
import org.openqa.selenium.PrintsPage;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeOptions;
import org.openqa.selenium.print.PrintOptions;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.time.Duration;
import java.util.Base64;
import java.util.Collections;
/**
* 調(diào)用vue頁面獲取PDF文件
*/
@Slf4j
@Service
public class WebAccessServerUtil {
@Value("${dllUrl:D:/DLL}")
private String dllUrl;
@Value("${webAddress:http://127.0.0.1:8080/mcpc/ui/dist/index.html}")
private String mWebAddress;
@Value("${chromePath:C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe}")
private String chromePath;
public boolean getPdfFromWeb(String cPath,String cpk_notification,String cpk_emgency){
log.info("根目錄:" + dllUrl);
log.info("Web端訪問地址:" + mWebAddress);
log.info("谷歌瀏覽器路徑:" + chromePath);
// 完全禁用WebDriverManager自動下載,手動指定驅(qū)動路徑(避免版本沖突)
System.setProperty("wdm.enabled", "false");
// 手動指定本地ChromeDriver路徑(替換為你的實(shí)際路徑)
//驅(qū)動版本需要和服務(wù)器瀏覽器版本對應(yīng) 138.0.7204.158
System.setProperty("webdriver.chrome.driver", dllUrl + File.separator + "chromedriver" + File.separator + "chromedriver.exe");
System.setProperty("webdriver.chrome.bin", chromePath);
// 配置瀏覽器選項(xiàng)(重點(diǎn)修復(fù))
ChromeOptions options = new ChromeOptions();
options.addArguments("--headless=new"); // 新版headless模式
options.addArguments("--disable-gpu");
options.addArguments("--no-sandbox");
options.addArguments("--disable-dev-shm-usage");
options.addArguments("--remote-allow-origins=*"); // 允許跨域WebSocket
options.setExperimentalOption("excludeSwitches", Collections.singletonList("enable-automation"));
WebDriver driver = new ChromeDriver(options);
WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(15)); // 最大等待10秒
try {
String vuePageUrl = mWebAddress + "#/generatePdfTest?pk_notification=" + cpk_notification + "&pk_emgency=" + cpk_emgency;
driver.get(vuePageUrl);
// 智能等待:直到頁面設(shè)置true
wait.until(ExpectedConditions.attributeToBe(
By.tagName("body"), "data-pdf-ready", "true"
));
PrintOptions printOptions = new PrintOptions();
printOptions.setBackground(true);
printOptions.setPageRanges("1-5");
// 替換原PDF字節(jié)處理代碼
Pdf pdf = ((PrintsPage) driver).print(printOptions);
String base64Pdf = pdf.getContent(); // 獲取Base64編碼的PDF內(nèi)容
byte[] pdfBytes = Base64.getDecoder().decode(base64Pdf); // 解碼為原始二進(jìn)制
Files.write(Paths.get(cPath), pdfBytes);
log.info("調(diào)用Web端生成PDF文件成功: " + cPath);
return true;
} catch (Exception e) {
log.error("獲取Web端PDF文件失敗:{}", e.getMessage(), e);
return false;
} finally {
try {
driver.quit();
} catch (Exception ignore) {
}
}
}
}五、前端代碼
前端vue僅供測試使用頁面,具體業(yè)務(wù)使用需要進(jìn)行美化和數(shù)據(jù)填充
在路由配置文件增加如下配置,實(shí)現(xiàn)路由和文件的綁定
{
path: '/generatePdfTest',
name: 'GeneratePDFTest',
component: (resolve) =>
require(['@/components/notificationRecord/MedicalNotificationPdf'], resolve),
}具體pdf生成頁面代碼如下
<template>
<div class="pdf-container">
<h1>{{ title }}</h1>
<div v-for="item in items" :key="item.id" class="item">
<p><strong>{{ item.label }}:</strong> {{ item.value }}</p>
</div>
</div>
</template>
<script>
export default {
data() {
return {
title: '測試通知記錄書',
items: []
}
},
mounted() {
try {
// 檢查路由是否可用(避免this.$route未定義的錯誤)
if (!this.$route) {
throw new Error("Vue Router未正確配置");
}
const recordId = this.$route.query.id || 'default';
// 模擬數(shù)據(jù)加載
setTimeout(() => {
this.items = [
{ id: 1, label: '患者姓名', value: '張三' },
{ id: 2, label: '病歷號', value: recordId },
{ id: 3, label: '診斷結(jié)果', value: 'stemi' },
{ id: 4, label: '診斷日期', value: '2025-08-12' },
{ id: 5, label: '診斷醫(yī)生', value: '李四' }
];
// 確認(rèn)body元素存在后再設(shè)置屬性
if (document.body) {
document.body.setAttribute('data-pdf-ready', 'true');
console.log("PDF渲染標(biāo)記已設(shè)置"); // 用于瀏覽器控制臺調(diào)試
} else {
console.error("body元素不存在,無法設(shè)置標(biāo)記");
}
}, 500);
} catch (error) {
console.error("組件加載錯誤:", error);
}
}
}
</script>
<style scoped>
/* 樣式保持不變 */
.pdf-container {
width: 210mm;
margin: 0 auto;
padding: 20px;
font-family: "SimSun";
}
.item {
margin: 10px 0;
}
</style>
六、最終效果
頁面查看網(wǎng)頁P(yáng)DF

后端調(diào)用生成的PDF

七、總結(jié)
該功能僅作為“曲線救國”方案,實(shí)際業(yè)務(wù)場景需要盡可能預(yù)想到該流程提前設(shè)計(jì)交互思路,供大家參考學(xué)習(xí)哈。
到此這篇關(guān)于Java調(diào)用Vue前端頁面生成PDF文件的文章就介紹到這了,更多相關(guān)Java調(diào)用Vue前端生成PDF文件內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
通過System.getProperty配置JVM系統(tǒng)屬性
這篇文章主要介紹了通過System.getProperty配置JVM系統(tǒng)屬性,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2020-10-10
GateWay路由規(guī)則與動態(tài)路由詳細(xì)介紹
這篇文章主要介紹了GateWay路由規(guī)則與GateWay動態(tài)路由,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-09-09
java實(shí)現(xiàn)利用String類的簡單方法讀取xml文件中某個標(biāo)簽中的內(nèi)容
下面小編就為大家?guī)硪黄猨ava實(shí)現(xiàn)利用String類的簡單方法讀取xml文件中某個標(biāo)簽中的內(nèi)容。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2016-12-12
SpringBoot中MybatisX插件的簡單使用教程(圖文)
MybatisX 是一款基于 IDEA 的快速開發(fā)插件,方便在使用mybatis以及mybatis-plus開始時簡化繁瑣的重復(fù)操作,本文主要介紹了SpringBoot中MybatisX插件的簡單使用教程,感興趣的可以了解一下2023-06-06
詳解Java 包掃描實(shí)現(xiàn)和應(yīng)用(Jar篇)
這篇文章主要介紹了詳解Java 包掃描實(shí)現(xiàn)和應(yīng)用(Jar篇),本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-07-07
FreeMarker如何調(diào)用Java靜態(tài)方法及靜態(tài)變量方法
這篇文章主要介紹了FreeMarker如何調(diào)用Java靜態(tài)方法及靜態(tài)變量方法,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2023-12-12

