EasyPoi介绍
EasyPoi是一个Java的Excel和Word处理库,主要用于将Java对象转换为Excel或Word文档,并且可以从Excel或Word文档中读取数据到Java对象。本文将重点介绍如何使用EasyPoi写Word文档。
- 引入EasyPoi依赖
<dependency> <groupId>cn.afterturn</groupId> <artifactId>easypoi-base</artifactId> <version>5.2.2</version> </dependency> <dependency> <groupId>cn.afterturn</groupId> <artifactId>easypoi-word</artifactId> <version>5.2.2</version> </dependency>
- 创建Word模板
可以使用Microsoft Word或其他能够创建Word模板的软件来设计Word文档的模板文件。模板文件中可以使用占位符来标记需要替换的内容。例如,在Word文档中可以插入占位符${name}表示需要替换的内容。
- 创建Java对象
使用Java对象来存放需要写入Word文档的数据。
@Data public class User { private String name; private int age; }
- 使用EasyPoi写入Word文档
创建一个WordExportUtil对象,利用模板和Java对象生成Word文档。
import cn.afterturn.easypoi.word.WordExportUtil; import org.apache.poi.xwpf.usermodel.XWPFDocument; import java.io.FileOutputStream; import java.util.HashMap; import java.util.Map; public class WriteWordDemo { public static void main(String[] args) throws Exception { // 创建模板和数据对象 User user = new User("Tom", 18); Map<String, Object> map = new HashMap<>(); map.put("name", user.getName()); map.put("age", user.getAge()); // 加载模板文件 XWPFDocument doc = WordExportUtil.exportWord07( "user_template.docx", map); // 输出文件 FileOutputStream fos = new FileOutputStream("user.docx"); doc.write(fos); fos.close(); doc.close(); } }
使用WordExportUtil.exportWord07方法可以生成.docx格式的Word文档,第一个参数为Word模板文件名,第二个参数是一个Map对象,其中包含了模板中所有的占位符和对应的数据。
基于EasyPoi实现段落循环
EasyPoi写word模版导出比较方便。但是如果你希望在写word模版的时候将段落进行循环,那就实现不了。下面的方法基于EasyPoi实现了word段落的循环。
public class WordParagraphHolder { private final Logger logger = LoggerFactory.getLogger(WordParagraphHolder.class); private XWPFDocument sourceDocument; private String targetFileName; private Map<String, Object> data; public WordParagraphHolder(XWPFDocument sourceDocument, String targetFileName, Map<String, Object> data) { this.sourceDocument = sourceDocument; this.targetFileName = targetFileName; this.data = data; } public String execute() throws Exception { MyXWPFDocument second = null; FileOutputStream fos2 = null; try { // 循环生成段落 Map<String, Object> data = this.data; this.parseAllParagraphic(sourceDocument.getParagraphs(), data); File targetFile = new File(targetFileName); FileOutputStream fos = FileUtils.openOutputStream(targetFile); sourceDocument.write(fos); // 关闭流 fos.close(); sourceDocument.close(); // 循环段落赋值 second = WordCache.getXWPFDocument(targetFile.getPath()); this.evalAllParagraphic(second.getParagraphs(), data); fos2 = FileUtils.openOutputStream(targetFile); second.write(fos2); } catch (Exception e) { logger.error("生成word段落失败:", e); return ""; } finally { // 关闭流 fos2.close(); second.close(); } return targetFileName; } private void evalAllParagraphic(List<XWPFParagraph> paragraphs, Map<String, Object> map) throws Exception { int size = paragraphs.size(); Map<String, Integer> indexMap = new HashMap<>(); for (int i = 0; i < size; ++i) { XWPFParagraph paragraph = (XWPFParagraph) paragraphs.get(i); logger.info("段落内容:{}", paragraph.getText()); if (paragraph.getText().indexOf("(") != -1) { Object obj = checkThisParagraphNeedIterator(paragraph, map); String listKey = getParagraphListKey(paragraph); Integer index = indexMap.getOrDefault(listKey, 0); if (Objects.nonNull(obj) && obj instanceof List && index < ((List) obj).size()) { Object o = ((List) obj).get(index); parseParagraph(paragraph, o); index++; indexMap.put(listKey, index); } } } } private void parseAllParagraphic(List<XWPFParagraph> paragraphs, Map<String, Object> map) throws Exception { int size = paragraphs.size(); List<XWPFParagraph> paragraphList = new ArrayList<>(); // 循环找出需要复制的段落 for (int i = 0; i < size; ++i) { XWPFParagraph paragraph = (XWPFParagraph) paragraphs.get(i); if (paragraph.getText().indexOf("(") != -1) { Object obj = checkThisParagraphNeedIterator(paragraph, map); if (Objects.nonNull(obj) && obj instanceof List) { paragraphList.add(paragraph); } } } // 复制段落 for (XWPFParagraph paragraph : paragraphList) { System.out.println(paragraph.getText()); Object obj = checkThisParagraphNeedIterator(paragraph, map); addParagraph(paragraph, (List) obj); // 删除模板段落 XWPFDocument document = paragraph.getDocument(); document.removeBodyElement(document.getPosOfParagraph(paragraph)); } } /** * 复制段落 * * @param source 原段落 * @param doc */ public XWPFParagraph createParagraph(XWPFParagraph source, XWPFDocument doc) { // 使用游标创建一个新行 XmlCursor cursor = source.getCTP().newCursor(); XWPFParagraph newParagraph = doc.insertNewParagraph(cursor); newParagraph.getCTP().set(source.getCTP().copy()); return newParagraph; } private Object checkThisParagraphNeedIterator(XWPFParagraph paragraph, Map<String, Object> map) throws Exception { String text = paragraph.getText().trim(); if (text != null && text.contains("fe:") && text.startsWith("(")) { text = text.replace("!fe:", "").replace("$fe:", "").replace("fe:", "").replace("(", ""); String[] keys = text.replaceAll("\s{1,}", " ").trim().split(" "); Object result = PoiPublicUtil.getParamsValue(keys[0], map); return Objects.nonNull(result) ? result : new ArrayList(0); } else { return null; } } private String getParagraphListKey(XWPFParagraph paragraph) throws Exception { String text = paragraph.getText().trim(); if (text != null && text.contains("fe:") && text.startsWith("(")) { text = text.replace("!fe:", "").replace("$fe:", "").replace("fe:", "").replace("(", ""); String[] keys = text.replaceAll("\s{1,}", " ").trim().split(" "); return keys[0]; } else { return null; } } /** * 赋值段落 * * @param paragraph * @param obj * @throws Exception */ public void parseParagraph(XWPFParagraph paragraph, Object obj) throws Exception { String listname = paragraph.getText().trim(); boolean contains = listname.contains("fe:"); if (!contains) { return; } Map<String, Object> objectMap = BeanUtil.beanToMap(obj); parseThisParagraph(paragraph, objectMap); } /** * 增加段落 * * @param paragraph * @param list * @throws Exception */ public void addParagraph(XWPFParagraph paragraph, List<Object> list) throws Exception { XWPFParagraph currentParagraph = paragraph; System.out.println("start for each data list :" + list.size()); Iterator var11 = list.iterator(); while (var11.hasNext()) { Object obj = var11.next(); this.createParagraph(currentParagraph, currentParagraph.getDocument()); } } /** * 遍历段落赋值 * * @param paragraph * @param map * @throws Exception */ private void parseThisParagraph(XWPFParagraph paragraph, Map<String, Object> map) throws Exception { XWPFRun currentRun = null; String currentText = ""; Boolean isfinde = false; List<Integer> runIndex = new ArrayList(); XWPFRun preRun = null; for (int i = 0; i < paragraph.getRuns().size(); ++i) { XWPFRun run = (XWPFRun) paragraph.getRuns().get(i); String text = run.getText(0); if (!StringUtils.isEmpty(text)) { if (isfinde) { currentText = currentText + text; if (currentText.indexOf("[") == -1) { isfinde = false; runIndex.clear(); } else { runIndex.add(i); } if (currentText.indexOf("]") != -1) { this.changeValues(paragraph, currentRun, currentText, runIndex, map); currentText = ""; isfinde = false; } } else if (text.indexOf("[") >= 0) { currentText = text; isfinde = true; currentRun = run; } else { currentText = ""; } if (currentText.indexOf("]") != -1) { this.changeValues(paragraph, currentRun, currentText, runIndex, map); isfinde = false; } } // 去除多余的字符串 if (!StringUtils.isEmpty(text)) { if (text.indexOf("(") != -1) { preRun = run; } else if (text.indexOf("$fe") != -1) { run.setText("", 0); // 清除第一个( preRun.setText("", 0); } // 清除最后一个) if (i == paragraph.getRuns().size() - 1) { if (text.indexOf(")") != -1) { if (text.length() >= 1) { String replace = text.replace(")", ""); run.setText(replace, 0); } } } } } } private void changeValues(XWPFParagraph paragraph, XWPFRun currentRun, String currentText, List<Integer> runIndex, Map<String, Object> map) throws Exception { Object obj = getRealValue(currentText, map); if (obj instanceof ImageEntity) { currentRun.setText("", 0); ExcelMapParse.addAnImage((ImageEntity) obj, currentRun); } else { currentText = obj.toString(); PoiPublicUtil.setWordText(currentRun, currentText); } for (int k = 0; k < runIndex.size(); ++k) { ((XWPFRun) paragraph.getRuns().get((Integer) runIndex.get(k))).setText("", 0); } runIndex.clear(); } public static Object getRealValue(String currentText, Map<String, Object> map) throws Exception { String params = ""; while (currentText.indexOf("[") != -1) { params = currentText.substring(currentText.indexOf("[") + 1, currentText.indexOf("]")); Object obj = PoiElUtil.eval(params.trim(), map); if (obj instanceof ImageEntity || obj instanceof List || obj instanceof ExcelListEntity) { return obj; } if (obj != null) { currentText = currentText.replace("[" + params + "]", obj.toString()); } else { currentText = currentText.replace("[" + params + "]", ""); } } return currentText; }
使用方法
新建word插入如下内容:
($fe:resultList [createDate],我使用[number]元,购买苹果[amount]个,截止日期[endDate],使用支付方式[type])
其中resultList 为入参list的名称,可以自由更换,[createDate]内createDate为list中字段的名称,其他的为固定格式。
执行下面的测试代码:
public static void main(String[] args) throws Exception { Map<String, Object> date = createDate(); String sourceFile = "d:/temp/模版word.docx"; String targetFile = "d:/temp/输出结果.docx"; MyXWPFDocument first = WordCache.getXWPFDocument(sourceFile); WordParagraphHolder test = new WordParagraphHolder(first, targetFile, date); test.execute(); } private static Map<String, Object> createDate() { //填充数据 List<WordExportBatch> resultList = new ArrayList<>(); WordExportBatch wordExport = new WordExportBatch(); WordExportBatch wordExport1 = new WordExportBatch(); wordExport.setCreateDate("2022/9/30"); wordExport1.setCreateDate("2022/9/28"); wordExport.setNumber("11"); wordExport1.setNumber("15"); wordExport.setAmount("1234.5"); wordExport1.setAmount("2345.77"); wordExport.setEndDate("2022/12/31"); wordExport1.setEndDate("2022/11/30"); wordExport.setType("支付宝"); wordExport1.setType("微信"); resultList.add(wordExport); resultList.add(wordExport1); //准备数据 Map<String, Object> params = new HashMap<>(); params.put("resultList", resultList); return params; }
输出: