Java文件處理之使用XWPFDocument導(dǎo)出Word文檔
一、前言
在Java項(xiàng)目開發(fā)過程中經(jīng)常會(huì)遇到導(dǎo)出Word文檔的業(yè)務(wù)場(chǎng)景。XWPFDocument是apache基金會(huì)提供的用戶導(dǎo)出Word文檔的工具類。
二、基本的概念
- XWPFDocument:代表一個(gè)docx文檔
- XWPFParagraph:代表文檔、表格、標(biāo)題等各種的段落,由多個(gè)XWPFRun組成
- XWPFRun:代表具有同樣風(fēng)格的一段文本
- XWPFTable:代表一個(gè)表格
- XWPFTableRow:代表表格的一行
- XWPFTableCell:代表表格的一個(gè)單元格
- XWPFChar:表示.docx文件中的圖表
- XWPFHyperlink:表示超鏈接
- XWPFPicture:代表圖片
- XWPFComment :代表批注
- XWPFFooter:代表頁腳
- XWPFHeader:代表頁眉
- XWPFStyles:樣式(設(shè)置多級(jí)標(biāo)題的時(shí)候用)
三、Maven依賴(JAR)
<!-- poi pdf文件/xml文件 --> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-ooxml</artifactId> <version>3.10-FINAL</version> </dependency> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi</artifactId> <version>4.1.2</version> </dependency>
四、Word模板
1.正文段落
一個(gè)文檔包含多個(gè)段落,一個(gè)段落包含多個(gè)Runs,一個(gè)Runs包含多個(gè)Run,Run是文檔的最小單元
獲取所有段落:List paragraphs = word.getParagraphs();
獲取一個(gè)段落中的所有Runs:List xwpfRuns = xwpfParagraph.getRuns();
獲取一個(gè)Runs中的一個(gè)Run:XWPFRun run = xwpfRuns.get(index);
XWPFRun–代表具有相同屬性的一段文本
2.正文表格
一個(gè)文檔包含多個(gè)表格,一個(gè)表格包含多行,一行包含多列(格),每一格的內(nèi)容相當(dāng)于一個(gè)完整的文檔
獲取所有表格:List xwpfTables = doc.getTables();
獲取一個(gè)表格的行數(shù):int rcount = xwpfTable.getNumberOfRows();
獲取一個(gè)表格的第幾行:XWPFTableRow row = table.getRow(i);
獲取一個(gè)表格中的所有行:List xwpfTableRows = xwpfTable.getRows();
獲取一行中的所有列:List xwpfTableCells = xwpfTableRow.getTableCells();
獲取一格里的內(nèi)容:List paragraphs = xwpfTableCell.getParagraphs();
之后和正文段落一樣
注:
- 表格的一格相當(dāng)于一個(gè)完整的docx文檔,只是沒有頁眉和頁腳。里面可以有表格,使用xwpfTableCell.getTables()獲取,and so on
- 在poi文檔中段落和表格是完全分開的,如果在兩個(gè)段落中有一個(gè)表格,在poi中是沒辦法確定表格在段落中間的。(當(dāng)然除非你本來知道了,這句是廢話)。只有文檔的格式固定,才能正確的得到文檔的結(jié)構(gòu)
3.頁眉
一個(gè)文檔可以有多個(gè)頁眉,頁眉里面可以包含段落和表格
獲取文檔的頁眉:List headerList = doc.getHeaderList();
獲取頁眉里的所有段落:List paras = header.getParagraphs();
獲取頁眉里的所有表格:List tables = header.getTables();
4.頁腳
頁腳和頁眉基本類似,可以獲取表示頁數(shù)的角標(biāo)
五、XWPFDocument的使用
5.4導(dǎo)出Word文檔
1.word模板
在resources目錄下準(zhǔn)備好word模板:xiaoshen.docx
2.PdfTest測(cè)試類
package com.shenxm.file.pdf.test; import com.shenxm.file.pdf.service.impl.SystemFileBizImpl; import org.junit.Test; import java.text.SimpleDateFormat; import java.util.Date; import java.util.HashMap; /** * @Author: shenxm * @Description: pdf測(cè)試 * @Version 1.0 */ public class PdfTest { @Test public void test1(){ Date date = new Date(); SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss"); String format = simpleDateFormat.format(date); HashMap<String, Object> boMap = new HashMap<>(); boMap.put("address","北京"); boMap.put("name","法克\n蒙克麗麗\n娜娜"); boMap.put("datetime","\n"+format); boMap.put("opinion","\n小沈\n"+format+"\n審批通過"); boMap.put("book","春的林野"); String docxTemplate = "xiaoshen.docx";//docx模板 String pdfFileName = "xiaoshen.pdf";//輸出的pdf SystemFileBizImpl systemFileBiz = new SystemFileBizImpl(); systemFileBiz.exportPdf(boMap,docxTemplate,pdfFileName); } }
3.ISystemFileService接口
package com.shenxm.file.pdf.service; import com.shenxm.file.pdf.entity.DownloadFileBo; import java.util.Map; public interface ISystemFileService { DownloadFileBo exportPdf(Map<String,Object> map,String template,String fileName); }
4.SystemFileServiceImpl實(shí)現(xiàn)類
package com.shenxm.file.pdf.service.impl; import com.shenxm.file.pdf.entity.DownloadFileBo; import com.shenxm.file.pdf.service.ISystemFileService; import org.apache.poi.xwpf.usermodel.*; import org.springframework.stereotype.Service; import org.springframework.util.Assert; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.util.Iterator; import java.util.List; import java.util.Map; /** * @Author: shenxm * @Description: 文件處理 Service * @Version 1.0 */ @Service public class SystemFileServiceImpl implements ISystemFilService { @Override public DownloadFileBo exportPdf(Map<String, Object> map, String docxTemplateName, String pdfFileName) { //校驗(yàn)參數(shù) Assert.notEmpty(map, "數(shù)據(jù)源不可為空!"); Assert.notNull(docxTemplateName,"docxTemplateName不能為空"); Assert.notNull(pdfFileName,"pdfFileName不能為空"); String pdfExportPath = "G:" + File.separator + "test1" + File.separator; //1.生成pdf文件對(duì)象 File pdfFile = this.createFile(pdfExportPath, pdfFileName); String docxExportPath = "G:" + File.separator + "test1" + File.separator;//生成的word的路徑 String docxExportName ="xiaoshen1.docx"; //使用當(dāng)前線程的類加載器讀取文件 InputStream in = Thread.currentThread().getContextClassLoader().getResourceAsStream(docxTemplateName); if (in == null) { System.out.println("讀取文件失敗!"); } else { try { //讀取模板文檔 XWPFDocument document = new XWPFDocument(in); //替換段落中的${} this.replaceTextInParagragh(document, map); //替換表格中的${} this.replaceTextInTables(document, map); //TODO 替換其他的 //將Docx文檔寫入文件 File exportWord = new File(docxExportPath + docxExportName); FileOutputStream fileOutputStream = new FileOutputStream(exportWord); //輸出文件 document.write(fileOutputStream); fileOutputStream.flush(); //TODO word轉(zhuǎn)為pdf //關(guān)閉流 fileOutputStream.close(); in.close(); } catch (IOException e) { e.printStackTrace(); } } return null; } /** 替換表格中的占位符 */ private void replaceTextInTables(XWPFDocument document, Map<String, Object> dataMap) { //獲取所有的表格 List<XWPFTable> tables = document.getTables(); //循環(huán) for (XWPFTable table : tables) { //獲取每個(gè)表格的總行數(shù) int rcount = table.getNumberOfRows(); for (int i = 0; i < rcount; i++) { //獲取表格的第i行 XWPFTableRow row = table.getRow(i); //獲取一行的所有單元格 List<XWPFTableCell> cells = row.getTableCells(); for (XWPFTableCell cell : cells) { //一個(gè)cell相當(dāng)于一個(gè)document //獲取單元格內(nèi)的文本 String cellTextString = cell.getText(); //替換文本:${} -> value cellTextString = this.replaceText(cellTextString, dataMap); //移除表格中的段落 while (cell.getParagraphs().size() > 0) { cell.removeParagraph(0); } //處理換行,并設(shè)置單元格內(nèi)容 this.setWrap(cellTextString,cell); } } } } /** 替換段落中的占位符 */ private void replaceTextInParagragh(XWPFDocument document, Map<String, Object> dataMap) { //獲取整個(gè)Word所有段落:包含頁眉或頁腳文本的段落 List<XWPFParagraph> paragraphs = document.getParagraphs(); //循環(huán) for (XWPFParagraph paragragh : paragraphs) { //獲取一段的所有本文 List<XWPFRun> runs = paragragh.getRuns(); //獲取段落內(nèi)容:paragragh.getText(); //循環(huán) for (int i = 0; i < runs.size(); i++) { //XWPFRun--代表具有相同屬性的一段文本 XWPFRun xwpfRun = runs.get(i); //獲取文本中的內(nèi)容 String paraString = xwpfRun.getText(xwpfRun.getTextPosition()); if (paraString != null) { //替換文字 paraString = this.replaceText(paraString, dataMap); //設(shè)置替換后的段落 xwpfRun.setText(paraString, 0); } } } } /** 替換文字 */ private String replaceText(String text, Map<String, Object> dataMap) { String paraString = text; //遍歷map,將段落里面的${}替換成map里的value Iterator<Map.Entry<String, Object>> iterator = dataMap.entrySet().iterator(); while (iterator.hasNext()) { Map.Entry<String, Object> entry = iterator.next(); String key = entry.getKey(); String value = entry.getValue().toString(); //組裝map里的key為${key} StringBuffer sb = new StringBuffer(); String placeHolder = sb.append("${").append(key).append("}").toString(); //替換:將"${as}dasdas" --> value+dasdas paraString = paraString.replace(placeHolder, value); } return paraString; } /** 單元格內(nèi)設(shè)置換行 */ private void setWrap(String cellTextString,XWPFTableCell cell){ if (cellTextString != null &&cellTextString.trim().contains("\n")){ //創(chuàng)建文本 XWPFRun run = cell.addParagraph().createRun(); String[] split = cellTextString.split("\n"); run.setText(split[0],0); for (int i = 1; i < split.length; i++) { //添加換行符 run.addBreak(); //設(shè)置單元格內(nèi)容 run.setText(split[i]); } }else { //設(shè)置單元格內(nèi)容 cell.setText(cellTextString); } } /** 根據(jù)路徑和文件名 創(chuàng)建文件對(duì)象*/ private File createFile(String filePath,String fileName){ //pdf目錄對(duì)象 File file = new File(filePath); if (!file.exists() || !file.isDirectory()) { file.mkdirs(); } //pdf文件對(duì)象 StringBuffer filePathBuffer = new StringBuffer(); filePathBuffer.append(filePath).append(fileName); return new File(filePathBuffer.toString()); } }
5.結(jié)果
六、遇到問題
輸出為word的時(shí)候換行符無效
java換行符"\n"在word文檔中不生效,使用"\r",“\r\n”,“(char)11”,“^p”,“br”,“<w:br>”,"w:p"等均無法實(shí)現(xiàn)單元格內(nèi)換行的功能。
實(shí)現(xiàn)單元格內(nèi)自動(dòng)換行:
總結(jié)
到此這篇關(guān)于Java文件處理之使用XWPFDocument導(dǎo)出Word文檔的文章就介紹到這了,更多相關(guān)Java XWPFDocument導(dǎo)出Word文檔內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
java 輸入一個(gè)數(shù)字,反轉(zhuǎn)輸出這個(gè)數(shù)字的值(實(shí)現(xiàn)方法)
下面小編就為大家?guī)硪黄猨ava 輸入一個(gè)數(shù)字,反轉(zhuǎn)輸出這個(gè)數(shù)字的值(實(shí)現(xiàn)方法)。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2016-10-10Java實(shí)現(xiàn)圖書管理系統(tǒng)的示例代碼
這篇文章主要為大家詳細(xì)介紹了如何利用java語言實(shí)現(xiàn)簡(jiǎn)單的圖書管理系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-08-08Java?IO流與NIO技術(shù)綜合應(yīng)用詳細(xì)實(shí)例代碼
這篇文章主要給大家介紹了關(guān)于Java?IO流與NIO技術(shù)綜合應(yīng)用的相關(guān)資料,文中包括了字節(jié)流和字符流,以及它們的高級(jí)特性如緩沖區(qū)、序列化和反序列化,同時(shí)還介紹了NIO中的通道和緩沖區(qū),以及選擇器的使用,需要的朋友可以參考下2024-12-12java實(shí)現(xiàn)讀取帶合并單元格的Excel
這篇文章主要為大家詳細(xì)介紹了java如何實(shí)現(xiàn)讀取帶合并單元格的Excel,文中的示例代碼講解詳細(xì), 感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2023-12-12tk.mybatis如何擴(kuò)展自己的通用mapper
這篇文章主要介紹了tk.mybatis如何擴(kuò)展自己的通用mapper操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-06-06Spring實(shí)戰(zhàn)之ResourceLoader接口資源加載用法示例
這篇文章主要介紹了Spring實(shí)戰(zhàn)之ResourceLoader接口資源加載用法,結(jié)合實(shí)例形式分析了Spring使用ResourceLoader接口加載資源的相關(guān)配置與使用技巧,需要的朋友可以參考下2020-01-01java利用udp實(shí)現(xiàn)發(fā)送數(shù)據(jù)
這篇文章主要為大家詳細(xì)介紹了java利用udp實(shí)現(xiàn)發(fā)送數(shù)據(jù),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-07-07JavaWeb實(shí)現(xiàn)注冊(cè)用戶名檢測(cè)
這篇文章主要為大家詳細(xì)介紹了JavaWeb實(shí)現(xiàn)注冊(cè)用戶名檢測(cè),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-08-08