java如何根據(jù)提供word模板導(dǎo)出word文檔詳解
本文主要講解,利用poi-tl在word中動(dòng)態(tài)生成表格行,進(jìn)行文字、圖片填充。一共提供了兩種方式,1.基于本地文件 2.基于網(wǎng)絡(luò)文件
本文講解思路,1.先看示例,2. 示例對(duì)應(yīng)的代碼展示 3. 基本概念講解(api自行查閱文檔)。
這樣便于快速展示,不符合你的業(yè)務(wù)需求的可以直接劃走
1.效果示例圖
1.1 word模板
注意:這里提前闡述幾個(gè)概念。
- 基本上所有的變量都由{{}}包裹,交給poi-tl解析(變量命名隨便你,中文,英文都可以)
- {{工作經(jīng)歷列表}}下方的[序號(hào)]、[公司名]、[工作時(shí)長(zhǎng)]、[salary]、[聯(lián)系]、[崗位],就是使用了 poi-tl的表格行插件,動(dòng)態(tài)渲染表格行的語(yǔ)法,可以簡(jiǎn)單理解為此處表格對(duì)應(yīng)的每一行就是一個(gè)對(duì)象實(shí)體類,而{{工作經(jīng)歷列表}}就是一個(gè)list
- {{?申明項(xiàng)列表}} {{item}} {{/申明項(xiàng)列表}},此處則是使用了poi-tl的區(qū)塊對(duì)標(biāo)簽。區(qū)塊對(duì)在此處的,我只是用它來(lái)實(shí)現(xiàn)渲染同一個(gè)表格內(nèi)的多行文本渲染,可以理解為<申明項(xiàng)列表>標(biāo)簽對(duì)應(yīng)的就是一個(gè)list,< item >就是一個(gè)list中的一個(gè)個(gè)對(duì)象元素。并且此處會(huì)觸發(fā),換行后序號(hào)累加的word編排編號(hào)樣式。
- {{@員工簽字圖片}},此處需要注意,word模板中代表圖片的變量,必須要加上@特殊符號(hào)修飾!
1.2 參數(shù)替換后的word
看到這里,如果對(duì)應(yīng)的效果不滿足你的需求,那么你可以出門右轉(zhuǎn)了。
2.代碼展示
maven 依賴,使用poi-tl需要支持 poi高版本
<!-- POI 依賴 使用xlsx xml的格式(即XSSFWorkbook) --> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi</artifactId> <version>5.2.2</version> </dependency> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-ooxml</artifactId> <version>5.2.2</version> </dependency> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-scratchpad</artifactId> <version>3.17</version> </dependency> <!-- poi模板導(dǎo)入,主力包 --> <dependency> <groupId>com.deepoove</groupId> <artifactId>poi-tl</artifactId> <version>1.12.1</version> </dependency>
2.1 根據(jù)網(wǎng)絡(luò)生成word
/** * 根據(jù)網(wǎng)絡(luò)生成word **/ public static void generateByNetWork() { String ip = "192.168.x.x"; // 我這里是用文件服務(wù)器返回的網(wǎng)絡(luò)文件流,你也可以直接放置一個(gè)文件的url String pictureUrl = "http://" + ip + ":8080/api/file/download?id=b9e0ac0336a84fc0b4ebb4cdf2c805e0.png"; HashMap<String, Object> finalMap = new HashMap<>(); finalMap.put("姓名", "張三"); finalMap.put("身份證號(hào)", "511232XXXXXXX"); finalMap.put("手機(jī)號(hào)", "1328292xxxx"); finalMap.put("員工簽字", "張三"); finalMap.put("日期", "2023-05-16"); // 從網(wǎng)絡(luò)流讀取圖片,置入word模板,等待編譯 if (Validator.isNotEmpty(pictureUrl)) { PictureRenderData picture = Pictures.ofUrl(pictureUrl).size(40, 30).create(); finalMap.put("員工簽字圖片", picture); } ArrayList<Object> workList = CollUtil.newArrayList(); finalMap.put("工作經(jīng)歷列表", workList); for (int i = 0; i < 3; i++) { // 模擬從mysql查詢列表數(shù)據(jù) HashMap<String, Object> workItem = new HashMap<>(); workItem.put("序號(hào)", i + 1); workItem.put("公司名", "四川有限公司" + i); workItem.put("工作時(shí)長(zhǎng)", i + 10 + "小時(shí)"); workItem.put("salary", "800" + i); workItem.put("聯(lián)系", "項(xiàng)目經(jīng)理" + i); workItem.put("崗位", "java工程師" + i); workList.add(workItem); } // 注意:此處用的是 <區(qū)塊對(duì)> key是字符串,value則放置一個(gè)集合,類似于模板引擎的foreach標(biāo)簽 ArrayList<Object> stateList = CollUtil.newArrayList(); finalMap.put("申明項(xiàng)列表", stateList); // 模擬從mysql查詢數(shù)據(jù),改造為word模板所需的數(shù)據(jù)結(jié)構(gòu) List<String> stateListFromMySQL = Arrays.asList("本人所遞交的所有辦理人才引進(jìn)材料及填寫的情況均屬實(shí);" , "我已認(rèn)真閱讀以上內(nèi)容并確認(rèn);" , "若在申請(qǐng)期間信息變更不做變更。若違反,本人愿意承擔(dān)由此產(chǎn)生的后果。"); for (int i = 0; i < stateListFromMySQL.size(); i++) { HashMap<String, Object> stateItem = new HashMap<>(); // 此處在 每行文字后,都添加\n 的換行符。注意:此換行僅僅只是換行,并不等同于enter,不會(huì)觸發(fā)類似于 <在word中enter換行,自動(dòng)觸發(fā)編排序號(hào)累加> 的動(dòng)作 stateItem.put("item", stateListFromMySQL.get(i) + "\n"); stateList.add(stateItem); } // 從網(wǎng)絡(luò)url 下載word模板到指定文件夾 File wordTemplate = HttpUtil.downloadFileFromUrl("http://" + ip + ":8080/file/download" + "?id=talentsTemplate.docx", "D://upload" + File.separator); // 此處使用了poi-tl的<表格行循環(huán)插件>,此處一定要進(jìn)行參數(shù)bind,方便word模板參數(shù)替換 LoopRowTableRenderPolicy policy = new LoopRowTableRenderPolicy(); Configure build = Configure.builder().bind(policy, "工作經(jīng)歷列表").build(); // 進(jìn)行編譯 XWPFTemplate render = XWPFTemplate.compile(wordTemplate.getAbsolutePath(), build).render(finalMap); File word = new File("D://upload" + File.separator + IdUtil.getSnowflake().nextId() + ".docx"); try { // 關(guān)鍵步驟, render.writeToFile(word.getAbsolutePath()); } catch (IOException e) { throw new RuntimeException(e); } // 下面的方法可以根據(jù)業(yè)務(wù)調(diào)整,我這里是將參數(shù)替換后的word上傳到項(xiàng)目對(duì)應(yīng)的文件服務(wù)器,拿到的url進(jìn)行存入數(shù)據(jù)庫(kù) String json = HttpUtil.createPost("http://" + ip + ":8080/file/upload") .header("Content-Type", ContentType.MULTIPART.getValue()) .form("file", word) .execute() .body(); // hutool工具類,刪除 word。因?yàn)樵谶@個(gè)流程中,word只是一個(gè)中間產(chǎn)物,因?yàn)槲疑线呉呀?jīng)把該word 上傳到我的文件服務(wù)器,并且文件服務(wù)器 會(huì)返回給我它對(duì)應(yīng)的url FileUtil.del(word); List<String> list = JSONUtil.parseArray(json).toList(String.class); }
2.2 根據(jù)本地文件流生成word
/** * 根據(jù)本地文件流生成word **/ public static void generateByFile() { // 此處直接是本地文件的絕對(duì)路徑 String pictureUrl = "C://Users//x//Desktop//博客//word模板填參//generate//sign.png"; HashMap<String, Object> finalMap = new HashMap<>(); finalMap.put("姓名", "張三"); finalMap.put("身份證號(hào)", "511232XXXXXXX"); finalMap.put("手機(jī)號(hào)", "1328292xxxx"); finalMap.put("員工簽字", "張三"); finalMap.put("日期", "2023-05-16"); // 從本地讀取圖片,置入word模板,等待編譯 if (Validator.isNotEmpty(pictureUrl)) { PictureRenderData picture = Pictures.ofLocal(pictureUrl).size(60, 50).create(); finalMap.put("員工簽字圖片", picture); } ArrayList<Object> workList = CollUtil.newArrayList(); finalMap.put("工作經(jīng)歷列表", workList); for (int i = 0; i < 3; i++) { // 模擬從mysql查詢列表數(shù)據(jù) HashMap<String, Object> workItem = new HashMap<>(); workItem.put("序號(hào)", i + 1); workItem.put("公司名", "成都有限公司" + i); workItem.put("工作時(shí)長(zhǎng)", i + 5 + "小時(shí)"); workItem.put("salary", "1000" + i); workItem.put("聯(lián)系", "聯(lián)系人" + i); workItem.put("崗位", "崗位" + i); workList.add(workItem); } // 注意:此處用的是 <區(qū)塊對(duì)> key是字符串,value則放置一個(gè)集合,類似于模板引擎的foreach標(biāo)簽 ArrayList<Object> stateList = CollUtil.newArrayList(); finalMap.put("申明項(xiàng)列表", stateList); // 模擬從mysql查詢數(shù)據(jù),改造為word模板所需的數(shù)據(jù)結(jié)構(gòu) List<String> stateListFromMySQL = Arrays.asList("本人所遞交的所有辦理人才引進(jìn)材料及填寫的情況均屬實(shí);" , "我已認(rèn)真閱讀以上內(nèi)容并確認(rèn);" , "若在申請(qǐng)期間信息變更不做變更。若違反,本人愿意承擔(dān)由此產(chǎn)生的后果。"); for (int i = 0; i < stateListFromMySQL.size(); i++) { HashMap<String, Object> stateItem = new HashMap<>(); stateItem.put("item", stateListFromMySQL.get(i) + ""); stateList.add(stateItem); } // 從網(wǎng)絡(luò)url 下載word模板到指定文件夾 File wordTemplate = new File("C://Users//x//Desktop//博客//word模板填參//talentsTemplate.docx"); // 此處使用了poi-tl的<表格行循環(huán)插件>,此處一定要進(jìn)行參數(shù)bind,方便word模板參數(shù)替換 LoopRowTableRenderPolicy policy = new LoopRowTableRenderPolicy(); Configure build = Configure.builder().bind(policy, "工作經(jīng)歷列表").build(); // 進(jìn)行編譯 String absolutePath = wordTemplate.getAbsolutePath(); XWPFTemplate render = XWPFTemplate.compile(absolutePath, build).render(finalMap); // 此處是利用File,直接在本地創(chuàng)建文件,將參數(shù)替換后的文件流寫入到該文件,word就是最終的結(jié)果 File word = new File("C://Users//x//Desktop//博客//word模板填參//generate" + File.separator + IdUtil.getSnowflake().nextId() + ".docx"); try { render.writeToFile(word.getAbsolutePath()); } catch (IOException e) { throw new RuntimeException(e); } // 下面的方法可以根據(jù)業(yè)務(wù)調(diào)整,我這里是將參數(shù)替換后的word上傳到項(xiàng)目對(duì)應(yīng)的文件服務(wù)器,拿到的url進(jìn)行存入數(shù)據(jù)庫(kù) // FileUtil.del(word); // List<String> list = JSONUtil.parseArray(json).toList(String.class); }
2.3 方法調(diào)用
public static void main(String[] args) { // generateByNetWork(); generateByFile(); }
總結(jié): 上面的備注已經(jīng)說(shuō)的很清楚了,這邊不做過(guò)多解釋。如果有哪里看不懂的,評(píng)論區(qū)評(píng)論,我有空就來(lái)給你解答。
3. 基本介紹
此處不做api介紹,只做基本概念介紹,不需要的可以跳過(guò)了!官網(wǎng) http://deepoove.com/poi-tl/
poi-tl(poi template language)是Word模板引擎,使用Word模板和數(shù)據(jù)創(chuàng)建很棒的Word文檔。
方案 | 移植性 | 功能性 | 易用性 |
---|---|---|---|
Poi-tl | Java跨平臺(tái) | Word模板引擎,基于Apache POI,提供更友好的API | 低代碼,準(zhǔn)備文檔模板和數(shù)據(jù)即可 |
Apache POI | Java跨平臺(tái) | Apache項(xiàng)目,封裝了常見的文檔操作,也可以操作底層XML結(jié)構(gòu) | 文檔不全,這里有一個(gè)教程:Apache POI Word快速入門 |
Freemarker | XML跨平臺(tái) | 僅支持文本,很大的局限性 | 不推薦,XML結(jié)構(gòu)的代碼幾乎無(wú)法維護(hù) |
OpenOffice | 部署OpenOffice,移植性較差 | - | 需要了解OpenOffice的API |
HTML瀏覽器導(dǎo)出 | 依賴瀏覽器的實(shí)現(xiàn),移植性較差 | HTML不能很好的兼容Word的格式,樣式糟糕 | - |
Jacob、winlib | Windows平臺(tái) | - | 復(fù)雜,完全不推薦使用 |
3.1 功能介紹
3.2 版本說(shuō)明
本文博客使用的是poi-tl最新版,需要apache poi 5.1.0+,不然會(huì)報(bào)錯(cuò),no such method
3.3 快速開始
依賴引入
maven <dependency> <groupId>com.deepoove</groupId> <artifactId>poi-tl</artifactId> <version>1.12.1</version> </dependency> gradle implementation 'com.deepoove:poi-tl:1.12.1'
官網(wǎng)demo示例
XWPFTemplate template = XWPFTemplate.compile("template.docx").render( new HashMap<String, Object>(){{ put("title", "Hi, poi-tl Word模板引擎"); }}); template.writeAndClose(new FileOutputStream("output.docx"));
基本就是三部曲:
- compile 編譯模板
- render 渲染數(shù)據(jù)
- write 輸出到流
TDO模式:Template + data-model = output
最后再次貼出官網(wǎng)鏈接,http://deepoove.com/poi-tl/#_why_poi_tl
總結(jié)
到此這篇關(guān)于java如何根據(jù)提供word模板導(dǎo)出word文檔的文章就介紹到這了,更多相關(guān)java導(dǎo)出word文檔內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java8生成時(shí)間方式及格式化時(shí)間的方法實(shí)例
這篇文章主要給大家介紹了關(guān)于Java8生成時(shí)間方式及格式化時(shí)間的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-08-08Struts2學(xué)習(xí)教程之輸入校驗(yàn)示例詳解
這篇文章主要給大家介紹了關(guān)于Struts2學(xué)習(xí)教程之輸入校驗(yàn)的相關(guān)資料,文中通過(guò)示例介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用struts2具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2018-05-05java連接orcale數(shù)據(jù)庫(kù)示例分享
這篇文章主要介紹了java連接orcale數(shù)據(jù)庫(kù)示例,需要的朋友可以參考下2014-02-02基于javaMybatis存進(jìn)時(shí)間戳的問(wèn)題
這篇文章主要介紹了javaMybatis存進(jìn)時(shí)間戳的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-06-06SpringBoot多租戶配置與實(shí)現(xiàn)示例
本文詳細(xì)介紹了在SpringBoot中實(shí)現(xiàn)多租戶架構(gòu)的方法和步驟,包括配置數(shù)據(jù)源、Hibernate攔截器、租戶解析器等,以共享數(shù)據(jù)庫(kù)、共享數(shù)據(jù)表的方式,確保數(shù)據(jù)隔離和安全性,感興趣的可以了解一下2024-09-09SpringBoot集成selenium實(shí)現(xiàn)自動(dòng)化測(cè)試的代碼工程
Selenium?是支持web?瀏覽器自動(dòng)化的一系列工具和[庫(kù)]?它提供了擴(kuò)展來(lái)模擬用戶與瀏覽器的交互,用于擴(kuò)展瀏覽器分配的分發(fā),本文給大家介紹了SpringBoot集成selenium實(shí)現(xiàn)自動(dòng)化測(cè)試的代碼工程,需要的朋友可以參考下2024-08-08java設(shè)計(jì)日歷可視化的詳細(xì)步驟記錄
這篇文章主要給大家介紹了關(guān)于java設(shè)計(jì)日歷可視化的相關(guān)資料,通過(guò)自定義的CircleLabel類來(lái)突出顯示今天的日期,并使用BorderLayout布局管理窗口組件,文章詳細(xì)描述了各個(gè)類和方法的設(shè)計(jì)思想和實(shí)現(xiàn)邏輯,需要的朋友可以參考下2024-12-12解決idea報(bào)錯(cuò) Connot resolve column 的問(wèn)題
這篇文章主要介紹了解決idea報(bào)錯(cuò) Connot resolve column 的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2021-02-02MyBatis-Plus多數(shù)據(jù)源的示例代碼
本文主要介紹了MyBatis-Plus多數(shù)據(jù)源的示例代碼,包括依賴配置、數(shù)據(jù)源配置、Mapper 和 Service 的定義,具有一定的參考價(jià)值,感興趣的可以了解一下2024-05-05