使用java Apache poi 根据word模板生成word报表
使用poi读取word模板,替换word中的{text}标签,并根据自定义标签循环生成表格或表格中的行。
代码示例下载:https://download.csdn.net/download/u012775558/10306308
注意,仅支持docx格式的word文件,大概是word2010及以后版本,doc格式不支持。
word模板需要有固定的格式
这是表格内部循环生成行的例子。
注意:
1.表格第一行通过合并单元格的方式,设置为只有两个单元格,第一个单元格填写foreachTableRow标签,第二个单元格填写要替换的数据List名称,本例中是table1或table2,注意名称要和你后台wordDataMap中存入的key值相同。
2.表格第二行是表头。
3.表格第三行需要通过合并单元格的方式,设置为只有一个单元格,填写上foreachRows标签,代表从这一行以下开始循环替换。
4.表格第四行是要替换的数据,在map中的key值。
你也可以给行加上序号,但是不能直接输入序号,而是通过word的插入编号的功能插入编号,生成的表格才会有编号。
可以给表格加上表头和表尾数据,只需要把数据放入parametersMap(存储报表中不循环的数据)中即可。
这是循环生成表格的例子。
注意:
1.表格第一行通过合并单元格的方式,设置为只有两个单元格,第一个单元格填写foreachTable标签,第二个单元格填写要替换的数据List名称,本例中是table1。
2.表格其他部分只需要将要替换的数据用标签替换即可。
效果图如下:
- 模板文件
使用maven搭建项目,引入poi相关jar包。
<groupId>org.apache.poigroupId>
<artifactId>poiartifactId>
<version>3.13version>
dependency>
<dependency>
<groupId>org.apache.poigroupId>
<artifactId>poi-ooxmlartifactId>
<version>3.13version>
dependency>
代码如下
/**
* @Title: WordTemplate2.java
* @Package: com.highdata.templateTools
* @Description: TODO
* @author: Juveniless
* @date: 2017年11月27日 下午3:23:13
*/
package com.hidata.tool;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.poi.xwpf.usermodel.BodyElementType;
import org.apache.poi.xwpf.usermodel.IBodyElement;
import org.apache.poi.xwpf.usermodel.PositionInParagraph;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
import org.apache.poi.xwpf.usermodel.XWPFRun;
import org.apache.poi.xwpf.usermodel.XWPFTable;
import org.apache.poi.xwpf.usermodel.XWPFTableCell;
import org.apache.poi.xwpf.usermodel.XWPFTableRow;
/**
*
* 对docx文件中的文本及表格中的内容进行替换 --模板仅支持对 {key} 标签的替换
*
* @ClassName: WordTemplate
* @Description: TODO(!!!使用word2013 docx文件)
* @author Juveniless
* @date: 2017年11月27日 下午3:25:56
*
(1)word模板注意页边距的问题,存在问题:比如页边距默认为3cm,画表格时,仍然可以通过
* 拖拽,把表格边框拖动到看起来就像页边距只有1cm的样子,但是实际上此时页边距还是3cm,生成的
* word报表的页边距还是会按照3cm来生成。解决办法,在word文件里,设置好页边距,如果需要表格
* 两边页边距很窄,需要在word里设置页边距窄一点,而不是直接拖动表格边框来实现。
*
*/
public class WordTemplate {
private XWPFDocument document;
public XWPFDocument getDocument() {
return document;
}
public void setDocument(XWPFDocument document) {
this.document = document;
}
/**
* 初始化模板内容
*
* @author Juveniless
* @date 2017年11月27日 下午3:59:22
* @param inputStream
* 模板的读取流(docx文件)
* @throws IOException
*
*/
public WordTemplate(InputStream inputStream) throws IOException {
document = new XWPFDocument(inputStream);
}
/**
* 将处理后的内容写入到输出流中
*
* @param outputStream
* @throws IOException
*/
public void write(OutputStream outputStream) throws IOException {
document.write(outputStream);
}
/**
* 根据dataMap对word文件中的标签进行替换;
* !!!!***需要注意dataMap的数据格式***!!!!
* 对于需要替换的普通标签数据标签(不需要循环)-----必须在dataMap中存储一个key为parametersMap的map,
* 来存储这些不需要循环生成的数据,比如:表头信息,日期,制表人等。
* 对于需要循环生成的表格数据------key自定义,value为 --ArrayList<Map<String, String>>
* @author Juveniless
* @date 2017年11月27日 下午3:29:27
* @param dataMap
*
*/
public void replaceDocument(Map dataMap) {
if (!dataMap.containsKey("parametersMap")) {
System.out.println("数据源错误--数据源(parametersMap)缺失");
return;
}
@SuppressWarnings("unchecked")
Map parametersMap = (Map) dataMap
.get("parametersMap");
List bodyElements = document.getBodyElements();
int templateBodySize = bodyElements.size();
int curT = 0;
int curP = 0;
for (int a = 0; a < templateBodySize; a++) {
IBodyElement body = bodyElements.get(a);
if (BodyElementType.TABLE.equals(body.getElementType())) {
XWPFTable table = body.getBody().getTableArray(curT);
List tables = body.getBody().getTables();
table = tables.get(curT);
if (table != null) {
List tableCells = table.getRows().get(0).getTableCells();
String tableText = table.getText();
if (tableText.indexOf("##{foreach") > -1) {
if (tableCells.size() != 2
|| tableCells.get(0).getText().indexOf("##{foreach") < 0
|| tableCells.get(0).getText().trim().length() == 0) {
System.out
.println("文档中第"
+ (curT + 1)
+ "个表格模板错误,模板表格第一行需要设置2个单元格,"
+ "第一个单元格存储表格类型(##{foreachTable}## 或者 ##{foreachTableRow}##),第二个单元格定义数据源。");
return;
}
String tableType = tableCells.get(0).getText();
String dataSource = tableCells.get(1).getText();
System.out.println("读取到数据源:"+dataSource);
if (!dataMap.containsKey(dataSource)) {
System.out.println("文档中第" + (curT + 1) + "个表格模板数据源缺失");
return;
}
@SuppressWarnings("unchecked")
List
package com.hidata.tool;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class Test {
public static void main(String[] args) throws IOException {
Map<String, Object> wordDataMap = new HashMap<String, Object>();
Map<String, Object> parametersMap = new HashMap<String, Object>();
ListString, Object>> table1 = new ArrayListString, Object>>();
Map<String, Object> map1=new HashMap<>();
map1.put("name", "张三");
map1.put("age", "23");
map1.put("email", "[email protected]");
Map<String, Object> map2=new HashMap<>();
map2.put("name", "李四");
map2.put("age", "45");
map2.put("email", "[email protected]");
Map<String, Object> map3=new HashMap<>();
map3.put("name", "Tom");
map3.put("age", "34");
map3.put("email", "[email protected]");
table1.add(map1);
table1.add(map2);
table1.add(map3);
ListString, Object>> table2 = new ArrayListString, Object>>();
Map<String, Object> map4=new HashMap<>();
map4.put("name", "tom");
map4.put("number", "sd1234");
map4.put("address", "上海");
Map<String, Object> map5=new HashMap<>();
map5.put("name", "seven");
map5.put("number", "sd15678");
map5.put("address", "北京");
Map<String, Object> map6=new HashMap<>();
map6.put("name", "lisa");
map6.put("number", "sd9078");
map6.put("address", "广州");
table2.add(map4);
table2.add(map5);
table2.add(map6);
parametersMap.put("userName", "JUVENILESS");
parametersMap.put("time", "2018-03-24");
parametersMap.put("sum", "3");
wordDataMap.put("table1", table1);
wordDataMap.put("table2", table2);
wordDataMap.put("parametersMap", parametersMap);
File file = new File("D:\\Workspaces\\Eclipse 2017\\wordTemplate\\doc\\模板.docx");
FileInputStream fileInputStream = new FileInputStream(file);
WordTemplate template = new WordTemplate(fileInputStream);
template.replaceDocument(wordDataMap);
File outputFile=new File("D:\\Workspaces\\Eclipse 2017\\wordTemplate\\doc\\输出.docx");
FileOutputStream fos = new FileOutputStream(outputFile);
template.getDocument().write(fos);
}
}