[Java] Poi-ti根據word 模板template 生成新的word 文檔 替換文字

KouWei.Lee
11 min readJul 20, 2021

--

製作動機

因為工作上遇到根據給固定的Word 模板生成相關報告的需求,選用Poi-ti主要是因為該API算是在原Apache POI的Word模板引擎升級版,非常易用.

Poi-tl 簡介

Poi-tl是Word模板引擎透過標籤替換快速產生word 文件

以下為核心api code

XWPFTemplate template = XWPFTemplate.compile("~/file.docx").render(datas);

所有的語法結構都是以{{ 開始,以}} 結束

實作checklist

  1. 引入依賴
  2. 準備一份Word模板文件
  3. 調用Poi-ti-Api

Getting Started

1.1引入Maven 依賴

<dependency>
<groupId>com.deepoove</groupId>
<artifactId>poi-tl</artifactId>
<version>1.10.0</version>
</dependency>

1.2使用Gradle則是

compile group: 'com.deepoove', name: 'poi-tl', version: '1.10.0'

2.1準備一份Word模板文件

如下預期能頒發一張由特殊樣式圖片、姓名、日期構成的證書獎狀

2.2製作模板,包含標籤 {{標籤替換}}

poi是一款透過標籤替換達到快速產生word

採用TDO模式:Template + data-model = output

因此我們需要先製作模板

所有的標籤都是以{{開頭,以}}結尾,內容則是你想替換的變數自訂

新建文檔certificate.docx,包含文本{{Name}} ,{{year}},{{month}} 等等

3.1替换文字與使用Poi-ti-核心api

定義map完成指定標籤替換

//處理日期轉換成民國Calendar c = Calendar.getInstance();c.setTimeInMillis(System.currentTimeMillis());int year = c.get(Calendar.YEAR) - 1911;int month = c.get(Calendar.MONTH) + 1;int day = c.get(Calendar.DAY_OF_MONTH);System.out.print("民國" + year + "年");System.out.print(month + "月");System.out.println(day + "日");
// 用一個map將標籤替換指定文字Map<String, String> testMap = new HashMap<String, String>();testMap.put("Name", "王大大");testMap.put("year", Integer.toString(year));testMap.put("month", Integer.toString(month));testMap.put("day", Integer.toString(day));testMap.put("minister", "Kumamon");

接著調用核心api

替換對應文字

XWPFTemplate template = XWPFTemplate.compile(templatePath).render(testMap);

寫出檔案至我的桌面

template.writeAndClose(new FileOutputStream("/Users/guowei/Desktop/我的證書.docx"));

可以在桌面看到產生出的 我的證書.docx

並且姓名 日期 部長皆以替換成功

完成!

了解怎麼使用poi-ti 後那我們就可以應用他透過替換快速產生word 文件

製作出可讓使用者輸入內容的動態產生證書的網頁

製作可產生自定義證書 網站

前端

設定post到generateCertificate

<form method="POST" action="/generateCertificate">
<div class="input-group">
<input class="input--style-1" type="text" placeholder="姓名"
name="name">
</div>
<div class="input-group">
<input class="input--style-1" type="text" placeholder="部長"
name="minister">
</div>
<div class="input-group">
<input class="input--style-1" type="text" placeholder="證書名稱"
name="certificationName">
</div>
<div class="p-t-20">
<button class="btn btn--radius btn--green" type="submit">產生</button>
</div>
</form>

下載檔案程式Controller在調用這隻

注意點

1.是classpath in jar 讀取時不可使用getfile

2.Safai瀏覽器中文字變亂碼須轉換

可以參考這邊

//替換寫入並下載檔案
public static void downloadReportFile(String templatePath, String fileName, Map<String, String> replaceMap,
HttpServletResponse response, HttpServletRequest request) throws UnsupportedEncodingException {
System.out.println("進入下載");XWPFDocument document = null;InputStream in = null;// 生成新的word
response.reset();
response.setContentType("applicatoin/octet-stream");
ByteArrayOutputStream ostream = new ByteArrayOutputStream();
OutputStream os = null;
String userAgent = request.getHeader("User-Agent").toLowerCase();
System.out.println(userAgent);
if (userAgent.contains("safari")) {
// 處理safari中文字檔名造成亂碼
System.out.println("進到safari");
String safariFileName = new String(fileName.getBytes("utf-8"), "ISO-8859-1");// fileName.getBytes("UTF-8")處理safari的亂碼問題
response.addHeader("Content-Disposition", "attachment; filename=" + safariFileName);
} else {
response.addHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode(fileName, "UTF-8"));
}
try {System.out.println("執行生成Word");// 會使用Classpath原因:因為我們會包成jar檔部署到公有雲,打包後Spring會去訪問系統的路徑,但無法訪問JAR中的路徑InputStream()
in = new ClassPathResource(templatePath).getInputStream();
// 替換標籤文字
XWPFTemplate template = XWPFTemplate.compile(in).render(replaceMap);

System.out.println("替換完畢");
os = response.getOutputStream();
template.writeAndClose(os);
// 輸出到response
os.write(ostream.toByteArray());
in.close();
os.close();
ostream.close();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (in != null) {
in.close();
}
if (document != null) {
document.close();
}
if (os != null) {
os.close();
}
if (ostream != null) {
ostream.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}

最後複習一下

GKE部署

此次我們使用JIB 幫助我們更快的打包image

mvn clean install jib:build

這樣就把我們的image推送到dockerhub了

複習K8s文件撰寫

deployment.yaml

deployment.yamlapiVersion: apps/v1
kind: Deployment
metadata:
name: wordutil-deployment
labels:
app: poi-tl-demo
spec:
replicas: 1
selector:
matchLabels:
app: wordutil
template:
metadata:
labels:
app: wordutil
spec:
containers:
- name: wordutil
image: huway0310/wordutil:0.0.1-SNAPSHOT
env:
- name: "PORT"
value: "8080"
service.yamlapiVersion: v1
kind: Service
metadata:
name: wordutil
spec:
type: LoadBalancer
selector:
app: wordutil
ports:
- protocol: TCP
port: 60000
targetPort: 8080

部署可以參考這邊

最後可以看到我們成果

已部署到GKE 點擊連結可直接使用

程式源碼

希望這篇文章能夠幫助到你(妳)

如有錯誤指正

I hope you found this guide helpful. If not, then please let me know either in the comments below, I’m Albert

--

--