本文参考地址:https://blog.csdn.net/wangxiaoyingWXY/article/details/95377533
在参考文章的基础上,增加了扩展。感谢被参考的妹子。另外该博客主要记录很多poi操作word中遇到的问题和解决方式,所以会一直维护下去。

另外,我是在本地使用的wps,使用office的老哥(老姐)们可能在效果上有出入。

本文只是简单示例,仅做代码参考,在实际运用中可自行根据业务封装。

模板在示例代码中有的哈,在resource目录下面

还有就是博主真的很菜.....(手动滑稽)

关于示例代码:(码云地址)https://gitee.com/loverer/poi-demo

另外如果遇到模板标签不完整的情况,请参考:https://blog.csdn.net/weixin_43770790/article/details/86672962

说明:在poi3.x的版本中,没有对图表的支持,至少目前为止没有找到相应的文章介绍;在poi4.x的版本中加入了对图表的支持,所以需要引入poi4.x的依赖包,并且JDK最低版本要求8;

2019-12-04:新增:动态刷新内置Excel数据

2020-04-07:新增:更新插入的文本框内容(感谢博友:输过败过但不曾怕过啊)

2020-04-10:新增:新增一个定位图表的方式(感谢博友:输过败过但不曾怕过啊)

2020-04-11:新增:柱状图动态列,优化修改(兼容wps和office)(感谢:谈个长长的恋爱可好)

2020-04-21:新增:表格进行合并单元格,横竖都行,两种方式(感谢刘老师)

2020-04-23:解决问题:组合图页面显示的数据,跟第二列对应不上(感谢博友:吹古拉朽)

2020-06-06:解决问题:图表设置系列标题方式错误导致office无法打开的问题(感谢博友: qq_18900373)

// 设置标题
CTSerTx tx = ser.getTx();
tx.setV("阿里嘎痛");  // wps可以打开,office无法打开
tx.getStrRef().getStrCache().getPtList().get(0).setV("阿里嘎痛"); // wps和office都能打开

1.引入maven依赖包

<dependency><groupId>org.apache.poi</groupId><artifactId>poi</artifactId><version>4.1.0</version>
</dependency>
<dependency><groupId>org.apache.poi</groupId><artifactId>poi-ooxml</artifactId><version>4.1.0</version>
</dependency>

完整pom.xml内容

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.1.8.RELEASE</version><relativePath/> <!-- lookup parent from repository --></parent><groupId>com.example</groupId><artifactId>demo</artifactId><version>0.0.1-SNAPSHOT</version><name>demo</name><description>Demo project for Spring Boot</description><properties><java.version>1.8</java.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId><scope>runtime</scope><optional>true</optional></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>org.apache.poi</groupId><artifactId>poi</artifactId><version>4.1.0</version></dependency><dependency><groupId>org.apache.poi</groupId><artifactId>poi-ooxml</artifactId><version>4.1.0</version></dependency><!--2.poi官网指出需要poi4.x.x版本抛弃了jdk1.7之前的版本,所以适应此版本需要将jdk升级,如果不想升级还有另一种办法就是,使用springBoot单独做一个服务为你的主项目提供一个接口,让主项目去调用生成word流让主项目去接收即可。--></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build></project>

2.准备word模板文件,注意后缀是docx,模板中的图表可以有数据,但是最终会被代码替换,所以不重要

模板文件: 不知道怎么上传了,需要的私信吧,感谢;

3.两个类

类1:PoiDemoWordTable

package com.example.demo;import com.example.demo.util.PoiWordTools;import org.apache.poi.ooxml.POIXMLDocumentPart;
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.util.Units;
import org.apache.poi.xwpf.usermodel.*;
import org.apache.xmlbeans.XmlCursor;
import org.springframework.util.StringUtils;import java.io.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;public class PoiDemoWordTable {public static void main(String[] args) throws Exception {final String returnurl = "D:\\youxi\\jx\\result.docx";  // 结果文件final String templateurl = "D:\\youxi\\jx\\test.docx";  // 模板文件InputStream is = new FileInputStream(new File(templateurl));XWPFDocument doc = new XWPFDocument(is);// 替换word模板数据replaceAll(doc);// 保存结果文件try {File file = new File(returnurl);if (file.exists()) {file.delete();}FileOutputStream fos = new FileOutputStream(returnurl);doc.write(fos);fos.close();} catch (Exception e) {e.printStackTrace();}}/*** @Description: 替换段落和表格中*/public static void replaceAll(XWPFDocument doc) throws InvalidFormatException, IOException {doParagraphs(doc); // 处理段落文字数据,包括文字和表格、图片doCharts(doc);  // 处理图表数据,柱状图、折线图、饼图啊之类的}/*** 处理段落文字** @param doc* @throws InvalidFormatException* @throws FileNotFoundException* @throws IOException*/public static void doParagraphs(XWPFDocument doc) throws InvalidFormatException, IOException {// 文本数据Map<String, String> textMap = new HashMap<String, String>();textMap.put("var", "我是被替换的文本内容");// 图片数据Map<String, String> imgMap = new HashMap<String, String>();imgMap.put("img", "D:/youxi/ddd.png");/**----------------------------处理段落------------------------------------**/List<XWPFParagraph> paragraphList = doc.getParagraphs();if (paragraphList != null && paragraphList.size() > 0) {for (XWPFParagraph paragraph : paragraphList) {List<XWPFRun> runs = paragraph.getRuns();for (XWPFRun run : runs) {String text = run.getText(0);if (text != null) {// 替换文本信息String tempText = text;String key = tempText.replaceAll("\\{\\{", "").replaceAll("}}", "");if (!StringUtils.isEmpty(textMap.get(key))) {run.setText(textMap.get(key), 0);}// 替换图片内容 参考:https://blog.csdn.net/a909301740/article/details/84984445String tempImgText = text;String imgkey = tempImgText.replaceAll("\\{\\{@", "").replaceAll("}}", "");if (!StringUtils.isEmpty(imgMap.get(imgkey))) {String imgPath = imgMap.get(imgkey);try {run.setText("", 0);run.addPicture(new FileInputStream(imgPath), Document.PICTURE_TYPE_PNG, "img.png", Units.toEMU(200), Units.toEMU(200));} catch (Exception e) {e.printStackTrace();}}// 动态表格if (text.contains("${table1}")) {run.setText("", 0);XmlCursor cursor = paragraph.getCTP().newCursor();XWPFTable tableOne = doc.insertNewTbl(cursor);// ---这个是关键// 设置表格宽度,第一行宽度就可以了,这个值的单位,目前我也还不清楚,还没来得及研究tableOne.setWidth(8500);// 表格第一行,对于每个列,必须使用createCell(),而不是getCell(),因为第一行嘛,肯定是属于创建的,没有create哪里来的get呢XWPFTableRow tableOneRowOne = tableOne.getRow(0);//行new PoiWordTools().setWordCellSelfStyle(tableOneRowOne.getCell(0), "微软雅黑", "9", 0, "left", "top", "#000000", "#B4C6E7", "10%", "序号");new PoiWordTools().setWordCellSelfStyle(tableOneRowOne.createCell(), "微软雅黑", "9", 0, "left", "top", "#000000", "#B4C6E7", "45%", "公司名称(英文)");new PoiWordTools().setWordCellSelfStyle(tableOneRowOne.createCell(), "微软雅黑", "9", 0, "left", "top", "#000000", "#B4C6E7", "45%", "公司名称(中文)");// 表格第二行XWPFTableRow tableOneRowTwo = tableOne.createRow();//行new PoiWordTools().setWordCellSelfStyle(tableOneRowTwo.getCell(0), "微软雅黑", "9", 0, "left", "top", "#000000", "#B4C6E7", "10%", "一行一列");new PoiWordTools().setWordCellSelfStyle(tableOneRowTwo.getCell(1), "微软雅黑", "9", 0, "left", "top", "#000000", "#B4C6E7", "45%", "一行一列");new PoiWordTools().setWordCellSelfStyle(tableOneRowTwo.getCell(2), "微软雅黑", "9", 0, "left", "top", "#000000", "#B4C6E7", "45%", "一行一列");// ....... 可动态添加表格}}}}}}/*** 处理图表** @param doc* @throws FileNotFoundException*/public static void doCharts(XWPFDocument doc) throws FileNotFoundException {/**----------------------------处理图表------------------------------------**/// 数据准备List<String> titleArr = new ArrayList<String>();// 标题titleArr.add("title");titleArr.add("金额");List<String> fldNameArr = new ArrayList<String>();// 字段名fldNameArr.add("item1");fldNameArr.add("item2");// 数据集合List<Map<String, String>> listItemsByType = new ArrayList<Map<String, String>>();// 第一行数据Map<String, String> base1 = new HashMap<String, String>();base1.put("item1", "材料费用");base1.put("item2", "500");// 第二行数据Map<String, String> base2 = new HashMap<String, String>();base2.put("item1", "出差费用");base2.put("item2", "300");// 第三行数据Map<String, String> base3 = new HashMap<String, String>();base3.put("item1", "住宿费用");base3.put("item2", "300");listItemsByType.add(base1);listItemsByType.add(base2);listItemsByType.add(base3);// 获取word模板中的所有图表元素,用map存放// 为什么不用list保存:查看doc.getRelations()的源码可知,源码中使用了hashMap读取文档图表元素,// 对relations变量进行打印后发现,图表顺序和文档中的顺序不一致,也就是说relations的图表顺序不是文档中从上到下的顺序Map<String, POIXMLDocumentPart> chartsMap = new HashMap<String, POIXMLDocumentPart>();//动态刷新图表List<POIXMLDocumentPart> relations = doc.getRelations();for (POIXMLDocumentPart poixmlDocumentPart : relations) {if (poixmlDocumentPart instanceof XWPFChart) {  // 如果是图表元素String str = poixmlDocumentPart.toString();System.out.println("str:" + str);String key = str.replaceAll("Name: ", "").replaceAll(" - Content Type: application/vnd\\.openxmlformats-officedocument\\.drawingml\\.chart\\+xml", "").trim();System.out.println("key:" + key);chartsMap.put(key, poixmlDocumentPart);}}System.out.println("\n图表数量:" + chartsMap.size() + "\n");// 第一个图表-条形图POIXMLDocumentPart poixmlDocumentPart0 = chartsMap.get("/word/charts/chart1.xml");new PoiWordTools().replaceBarCharts(poixmlDocumentPart0, titleArr, fldNameArr, listItemsByType);// 第二个-柱状图POIXMLDocumentPart poixmlDocumentPart1 = chartsMap.get("/word/charts/chart2.xml");new PoiWordTools().replaceBarCharts(poixmlDocumentPart1, titleArr, fldNameArr, listItemsByType);// 第三个图表-多列柱状图doCharts3(chartsMap);// 第四个图表-折线图doCharts4(chartsMap);// 第五个图表-饼图POIXMLDocumentPart poixmlDocumentPart4 = chartsMap.get("/word/charts/chart5.xml");new PoiWordTools().replacePieCharts(poixmlDocumentPart4, titleArr, fldNameArr, listItemsByType);doCharts6(chartsMap);}public static void doCharts3(Map<String, POIXMLDocumentPart> chartsMap) {// 数据准备List<String> titleArr = new ArrayList<String>();// 标题titleArr.add("姓名");titleArr.add("欠款");titleArr.add("存款");List<String> fldNameArr = new ArrayList<String>();// 字段名fldNameArr.add("item1");fldNameArr.add("item2");fldNameArr.add("item3");// 数据集合List<Map<String, String>> listItemsByType = new ArrayList<Map<String, String>>();// 第一行数据Map<String, String> base1 = new HashMap<String, String>();base1.put("item1", "老张");base1.put("item2", (int)(1 + Math.random() * (100 - 1 + 1)) + "");base1.put("item3", (int)(1 + Math.random() * (100 - 1 + 1)) + "");// 第二行数据Map<String, String> base2 = new HashMap<String, String>();base2.put("item1", "老李");base2.put("item2", (int)(1 + Math.random() * (100 - 1 + 1)) + "");base2.put("item3", (int)(1 + Math.random() * (100 - 1 + 1)) + "");// 第三行数据Map<String, String> base3 = new HashMap<String, String>();base3.put("item1", "老刘");base3.put("item2", (int)(1 + Math.random() * (100 - 1 + 1)) + "");base3.put("item3", (int)(1 + Math.random() * (100 - 1 + 1)) + "");listItemsByType.add(base1);listItemsByType.add(base2);listItemsByType.add(base3);POIXMLDocumentPart poixmlDocumentPart2 = chartsMap.get("/word/charts/chart3.xml");new PoiWordTools().replaceBarCharts(poixmlDocumentPart2, titleArr, fldNameArr, listItemsByType);}public static void doCharts4(Map<String, POIXMLDocumentPart> chartsMap) {// 数据准备List<String> titleArr = new ArrayList<String>();// 标题titleArr.add("title");titleArr.add("占基金资产净值比例22222(%)");titleArr.add("额外的(%)");titleArr.add("额外的(%)");List<String> fldNameArr = new ArrayList<String>();// 字段名fldNameArr.add("item1");fldNameArr.add("item2");fldNameArr.add("item3");fldNameArr.add("item4");// 数据集合List<Map<String, String>> listItemsByType = new ArrayList<Map<String, String>>();// 第一行数据Map<String, String> base1 = new HashMap<String, String>();base1.put("item1", "材料费用");base1.put("item2", (int)(1 + Math.random() * (100 - 1 + 1)) + "");base1.put("item3", (int)(1 + Math.random() * (100 - 1 + 1)) + "");base1.put("item4", (int)(1 + Math.random() * (100 - 1 + 1)) + "");// 第二行数据Map<String, String> base2 = new HashMap<String, String>();base2.put("item1", "出差费用");base2.put("item2", (int)(1 + Math.random() * (100 - 1 + 1)) + "");base2.put("item3", (int)(1 + Math.random() * (100 - 1 + 1)) + "");base2.put("item4", (int)(1 + Math.random() * (100 - 1 + 1)) + "");// 第三行数据Map<String, String> base3 = new HashMap<String, String>();base3.put("item1", "住宿费用");base3.put("item2", (int)(1 + Math.random() * (100 - 1 + 1)) + "");base3.put("item3", (int)(1 + Math.random() * (100 - 1 + 1)) + "");base3.put("item4", (int)(1 + Math.random() * (100 - 1 + 1)) + "");listItemsByType.add(base1);listItemsByType.add(base2);listItemsByType.add(base3);POIXMLDocumentPart poixmlDocumentPart2 = chartsMap.get("/word/charts/chart4.xml");new PoiWordTools().replaceLineCharts(poixmlDocumentPart2, titleArr, fldNameArr, listItemsByType);}/*** 对应文档中的第6个图表(预处理—分公司情况)*/public static void doCharts6(Map<String, POIXMLDocumentPart> chartsMap) {// 数据准备List<String> titleArr = new ArrayList<String>();// 标题titleArr.add("title");titleArr.add("投诉受理量(次)");titleArr.add("预处理拦截工单量(次)");titleArr.add("拦截率");List<String> fldNameArr = new ArrayList<String>();// 字段名fldNameArr.add("item1");fldNameArr.add("item2");fldNameArr.add("item3");fldNameArr.add("item4");// 数据集合List<Map<String, String>> listItemsByType = new ArrayList<Map<String, String>>();// 第一行数据Map<String, String> base1 = new HashMap<String, String>();base1.put("item1", "通辽");base1.put("item2", (int)(1 + Math.random() * (100 - 1 + 1)) + "");base1.put("item3", (int)(1 + Math.random() * (100 - 1 + 1)) + "");base1.put("item4", (int)(1 + Math.random() * (100 - 1 + 1)) + "");// 第二行数据Map<String, String> base2 = new HashMap<String, String>();base2.put("item1", "呼和浩特");base2.put("item2", (int)(1 + Math.random() * (100 - 1 + 1)) + "");base2.put("item3", (int)(1 + Math.random() * (100 - 1 + 1)) + "");base2.put("item4", (int)(1 + Math.random() * (100 - 1 + 1)) + "");// 第三行数据Map<String, String> base3 = new HashMap<String, String>();base3.put("item1", "锡林郭勒");base3.put("item2", (int)(1 + Math.random() * (100 - 1 + 1)) + "");base3.put("item3", (int)(1 + Math.random() * (100 - 1 + 1)) + "");base3.put("item4", (int)(1 + Math.random() * (100 - 1 + 1)) + "");// 第四行数据Map<String, String> base4 = new HashMap<String, String>();base4.put("item1", "阿拉善");base4.put("item2", (int)(1 + Math.random() * (100 - 1 + 1)) + "");base4.put("item3", (int)(1 + Math.random() * (100 - 1 + 1)) + "");base4.put("item4", (int)(1 + Math.random() * (100 - 1 + 1)) + "");// 第五行数据Map<String, String> base5 = new HashMap<String, String>();base5.put("item1", "巴彦淖尔");base5.put("item2", (int)(1 + Math.random() * (100 - 1 + 1)) + "");base5.put("item3", (int)(1 + Math.random() * (100 - 1 + 1)) + "");base5.put("item4", (int)(1 + Math.random() * (100 - 1 + 1)) + "");// 第六行数据Map<String, String> base6 = new HashMap<String, String>();base6.put("item1", "兴安");base6.put("item2", (int)(1 + Math.random() * (100 - 1 + 1)) + "");base6.put("item3", (int)(1 + Math.random() * (100 - 1 + 1)) + "");base6.put("item4", (int)(1 + Math.random() * (100 - 1 + 1)) + "");// 第七行数据Map<String, String> base7 = new HashMap<String, String>();base7.put("item1", "乌兰察布");base7.put("item2", (int)(1 + Math.random() * (100 - 1 + 1)) + "");base7.put("item3", (int)(1 + Math.random() * (100 - 1 + 1)) + "");base7.put("item4", (int)(1 + Math.random() * (100 - 1 + 1)) + "");// 第八行数据Map<String, String> base8 = new HashMap<String, String>();base8.put("item1", "乌海");base8.put("item2", (int)(1 + Math.random() * (100 - 1 + 1)) + "");base8.put("item3", (int)(1 + Math.random() * (100 - 1 + 1)) + "");base8.put("item4", (int)(1 + Math.random() * (100 - 1 + 1)) + "");// 第九行数据Map<String, String> base9 = new HashMap<String, String>();base9.put("item1", "赤峰");base9.put("item2", (int)(1 + Math.random() * (100 - 1 + 1)) + "");base9.put("item3", (int)(1 + Math.random() * (100 - 1 + 1)) + "");base9.put("item4", (int)(1 + Math.random() * (100 - 1 + 1)) + "");// 第十行数据Map<String, String> base10 = new HashMap<String, String>();base10.put("item1", "包头");base10.put("item2", (int)(1 + Math.random() * (100 - 1 + 1)) + "");base10.put("item3", (int)(1 + Math.random() * (100 - 1 + 1)) + "");base10.put("item4", (int)(1 + Math.random() * (100 - 1 + 1)) + "");// 第十一行数据Map<String, String> base11 = new HashMap<String, String>();base11.put("item1", "呼伦贝尔");base11.put("item2", (int)(int)(1 + Math.random() * (100 - 1 + 1)) + "");base11.put("item3", (int)(1 + Math.random() * (100 - 1 + 1)) + "");base11.put("item4", (int)(1 + Math.random() * (100 - 1 + 1)) + "");// 第十二行数据Map<String, String> base12 = new HashMap<String, String>();base12.put("item1", "鄂尔多斯");base12.put("item2", (int)(1 + Math.random() * (100 - 1 + 1)) + "");base12.put("item3", (int)(1 + Math.random() * (100 - 1 + 1)) + "");base12.put("item4", (int)(1 + Math.random() * (100 - 1 + 1)) + "");listItemsByType.add(base1);listItemsByType.add(base2);listItemsByType.add(base3);listItemsByType.add(base4);listItemsByType.add(base5);listItemsByType.add(base6);listItemsByType.add(base7);listItemsByType.add(base8);listItemsByType.add(base9);listItemsByType.add(base10);listItemsByType.add(base11);listItemsByType.add(base12);// 下标0的图表-折线图POIXMLDocumentPart poixmlDocumentPart5 = chartsMap.get("/word/charts/chart6.xml");new PoiWordTools().replaceCombinationCharts(poixmlDocumentPart5, titleArr, fldNameArr, listItemsByType);}}

类2:PoiWordTools

package com.example.demo.util;import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.List;
import java.util.Map;import org.apache.poi.ooxml.POIXMLDocumentPart;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.apache.poi.xwpf.usermodel.XWPFChart;
import org.apache.poi.xwpf.usermodel.XWPFTableCell;
import org.openxmlformats.schemas.drawingml.x2006.chart.*;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTColor;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTFonts;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTHpsMeasure;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTJc;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTOnOff;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTP;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTPPr;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTParaRPr;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTR;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTRPr;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTShd;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTc;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTcPr;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTText;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTVerticalJc;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.STJc;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.STOnOff;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.STVerticalJc;/*** poi生成word的工具类* 针对于模板中的图表是静态的,也就是模板中的图表长什么样子不会根据数据而改变*/
public class PoiWordTools {private static final BigDecimal bd2 = new BigDecimal("2");/*** 调用替换柱状图数据*/public static void replaceBarCharts(POIXMLDocumentPart poixmlDocumentPart,List<String> titleArr, List<String> fldNameArr, List<Map<String, String>> listItemsByType) {XWPFChart chart = (XWPFChart) poixmlDocumentPart;chart.getCTChart();//根据属性第一列名称切换数据类型CTChart ctChart = chart.getCTChart();CTPlotArea plotArea = ctChart.getPlotArea();CTBarChart barChart = plotArea.getBarChartArray(0);List<CTBarSer> BarSerList = barChart.getSerList();  // 获取柱状图单位//刷新内置excel数据new PoiWordTools().refreshExcel(chart, listItemsByType, fldNameArr, titleArr);//刷新页面显示数据refreshBarStrGraphContent(barChart, BarSerList, listItemsByType, fldNameArr, 1);}/*** 调用替换折线图数据*/public static void replaceLineCharts(POIXMLDocumentPart poixmlDocumentPart,List<String> titleArr, List<String> fldNameArr, List<Map<String, String>> listItemsByType) {XWPFChart chart = (XWPFChart) poixmlDocumentPart;chart.getCTChart();//根据属性第一列名称切换数据类型CTChart ctChart = chart.getCTChart();CTPlotArea plotArea = ctChart.getPlotArea();CTLineChart lineChart = plotArea.getLineChartArray(0);List<CTLineSer> lineSerList = lineChart.getSerList();   // 获取折线图单位//刷新内置excel数据new PoiWordTools().refreshExcel(chart, listItemsByType, fldNameArr, titleArr);//刷新页面显示数据new PoiWordTools().refreshLineStrGraphContent(lineChart, lineSerList, listItemsByType, fldNameArr, 1);}/*** 调用替换饼图数据*/public static void replacePieCharts(POIXMLDocumentPart poixmlDocumentPart,List<String> titleArr, List<String> fldNameArr, List<Map<String, String>> listItemsByType) {XWPFChart chart = (XWPFChart) poixmlDocumentPart;chart.getCTChart();//根据属性第一列名称切换数据类型CTChart ctChart = chart.getCTChart();CTPlotArea plotArea = ctChart.getPlotArea();CTPieChart pieChart = plotArea.getPieChartArray(0);List<CTPieSer> pieSerList = pieChart.getSerList();  // 获取饼图单位//刷新内置excel数据new PoiWordTools().refreshExcel(chart, listItemsByType, fldNameArr, titleArr);//刷新页面显示数据new PoiWordTools().refreshPieStrGraphContent(pieChart, pieSerList, listItemsByType, fldNameArr, 1);}/*** 调用替换柱状图、折线图组合数据*/public static void replaceCombinationCharts(POIXMLDocumentPart poixmlDocumentPart,List<String> titleArr, List<String> fldNameArr, List<Map<String, String>> listItemsByType) {XWPFChart chart = (XWPFChart) poixmlDocumentPart;chart.getCTChart();//根据属性第一列名称切换数据类型CTChart ctChart = chart.getCTChart();CTPlotArea plotArea = ctChart.getPlotArea();CTBarChart barChart = plotArea.getBarChartArray(0);List<CTBarSer> barSerList = barChart.getSerList();  // 获取柱状图单位//刷新内置excel数据new PoiWordTools().refreshExcel(chart, listItemsByType, fldNameArr, titleArr);//刷新页面显示数据   数据中下标1开始的是柱状图数据,所以这个是1refreshBarStrGraphContent(barChart, barSerList, listItemsByType, fldNameArr, 1);CTLineChart lineChart = plotArea.getLineChartArray(0);List<CTLineSer> lineSerList = lineChart.getSerList();   // 获取折线图单位//刷新内置excel数据   有一个就可以了    有一个就可以了    有一个就可以了//new PoiWordTools().refreshExcel(chart, listItemsByType, fldNameArr, titleArr);//刷新页面显示数据   数据中下标3开始的是折线图的数据,所以这个是3new PoiWordTools().refreshLineStrGraphContent(lineChart, lineSerList, listItemsByType, fldNameArr, 3);}/*** 刷新折线图数据方法** @param typeChart* @param serList* @param dataList* @param fldNameArr* @param position* @return*/public static boolean refreshLineStrGraphContent(Object typeChart,List<?> serList, List<Map<String, String>> dataList, List<String> fldNameArr, int position) {boolean result = true;//更新数据区域for (int i = 0; i < serList.size(); i++) {//CTSerTx tx=null;CTAxDataSource cat = null;CTNumDataSource val = null;CTLineSer ser = ((CTLineChart) typeChart).getSerArray(i);//tx= ser.getTx();// Category Axis Datacat = ser.getCat();// 获取图表的值val = ser.getVal();// strData.setCTStrData strData = cat.getStrRef().getStrCache();CTNumData numData = val.getNumRef().getNumCache();strData.setPtArray((CTStrVal[]) null); // unset old axis textnumData.setPtArray((CTNumVal[]) null); // unset old values// set modellong idx = 0;for (int j = 0; j < dataList.size(); j++) {//判断获取的值是否为空String value = "0";if (new BigDecimal(dataList.get(j).get(fldNameArr.get(i + position))) != null) {value = new BigDecimal(dataList.get(j).get(fldNameArr.get(i + position))).toString();}if (!"0".equals(value)) {CTNumVal numVal = numData.addNewPt();//序列值numVal.setIdx(idx);numVal.setV(value);}CTStrVal sVal = strData.addNewPt();//序列名称sVal.setIdx(idx);sVal.setV(dataList.get(j).get(fldNameArr.get(0)));idx++;}numData.getPtCount().setVal(idx);strData.getPtCount().setVal(idx);//赋值横坐标数据区域String axisDataRange = new CellRangeAddress(1, dataList.size(), 0, 0).formatAsString("Sheet1", false);cat.getStrRef().setF(axisDataRange);//数据区域String numDataRange = new CellRangeAddress(1, dataList.size(), i + position, i + position).formatAsString("Sheet1", false);val.getNumRef().setF(numDataRange);// 设置系列生成方向}return result;}/*** 刷新柱状图数据方法** @param typeChart* @param serList* @param dataList* @param fldNameArr* @param position* @return*/public  static boolean refreshBarStrGraphContent(Object typeChart,List<?> serList, List<Map<String, String>> dataList, List<String> fldNameArr, int position) {boolean result = true;//更新数据区域for (int i = 0; i < serList.size(); i++) {
//            CTSerTx tx=null;CTAxDataSource cat = null;CTNumDataSource val = null;CTBarSer ser = ((CTBarChart) typeChart).getSerArray(i);
//            tx= ser.getTx();// Category Axis Datacat = ser.getCat();// 获取图表的值val = ser.getVal();// strData.setCTStrData strData = cat.getStrRef().getStrCache();CTNumData numData = val.getNumRef().getNumCache();strData.setPtArray((CTStrVal[]) null); // unset old axis textnumData.setPtArray((CTNumVal[]) null); // unset old values// set modellong idx = 0;for (int j = 0; j < dataList.size(); j++) {//判断获取的值是否为空String value = "0";if (new BigDecimal(dataList.get(j).get(fldNameArr.get(i + position))) != null) {value = new BigDecimal(dataList.get(j).get(fldNameArr.get(i + position))).toString();}if (!"0".equals(value)) {CTNumVal numVal = numData.addNewPt();//序列值numVal.setIdx(idx);numVal.setV(value);}CTStrVal sVal = strData.addNewPt();//序列名称sVal.setIdx(idx);sVal.setV(dataList.get(j).get(fldNameArr.get(0)));idx++;}numData.getPtCount().setVal(idx);strData.getPtCount().setVal(idx);//赋值横坐标数据区域String axisDataRange = new CellRangeAddress(1, dataList.size(), 0, 0).formatAsString("Sheet1", true);cat.getStrRef().setF(axisDataRange);//数据区域String numDataRange = new CellRangeAddress(1, dataList.size(), i + position, i + position).formatAsString("Sheet1", true);val.getNumRef().setF(numDataRange);}return result;}/*** 刷新饼图数据方法** @param typeChart* @param serList* @param dataList* @param fldNameArr* @param position* @return*/public static boolean refreshPieStrGraphContent(Object typeChart,List<?> serList, List<Map<String, String>> dataList, List<String> fldNameArr, int position) {boolean result = true;//更新数据区域for (int i = 0; i < serList.size(); i++) {//CTSerTx tx=null;CTAxDataSource cat = null;CTNumDataSource val = null;CTPieSer ser = ((CTPieChart) typeChart).getSerArray(i);//tx= ser.getTx();// Category Axis Datacat = ser.getCat();// 获取图表的值val = ser.getVal();// strData.setCTStrData strData = cat.getStrRef().getStrCache();CTNumData numData = val.getNumRef().getNumCache();strData.setPtArray((CTStrVal[]) null); // unset old axis textnumData.setPtArray((CTNumVal[]) null); // unset old values// set modellong idx = 0;for (int j = 0; j < dataList.size(); j++) {//判断获取的值是否为空String value = "0";if (new BigDecimal(dataList.get(j).get(fldNameArr.get(i + position))) != null) {value = new BigDecimal(dataList.get(j).get(fldNameArr.get(i + position))).toString();}if (!"0".equals(value)) {CTNumVal numVal = numData.addNewPt();//序列值numVal.setIdx(idx);numVal.setV(value);}CTStrVal sVal = strData.addNewPt();//序列名称sVal.setIdx(idx);sVal.setV(dataList.get(j).get(fldNameArr.get(0)));idx++;}numData.getPtCount().setVal(idx);strData.getPtCount().setVal(idx);//赋值横坐标数据区域String axisDataRange = new CellRangeAddress(1, dataList.size(), 0, 0).formatAsString("Sheet1", true);cat.getStrRef().setF(axisDataRange);//数据区域String numDataRange = new CellRangeAddress(1, dataList.size(), i + position, i + position).formatAsString("Sheet1", true);val.getNumRef().setF(numDataRange);}return result;}/*** 刷新内置excel数据** @param chart* @param dataList* @param fldNameArr* @param titleArr* @return*/public static boolean refreshExcel(XWPFChart chart,List<Map<String, String>> dataList, List<String> fldNameArr, List<String> titleArr) {boolean result = true;Workbook wb = new XSSFWorkbook();Sheet sheet = wb.createSheet("Sheet1");//根据数据创建excel第一行标题行for (int i = 0; i < titleArr.size(); i++) {if (sheet.getRow(0) == null) {sheet.createRow(0).createCell(i).setCellValue(titleArr.get(i) == null ? "" : titleArr.get(i));} else {sheet.getRow(0).createCell(i).setCellValue(titleArr.get(i) == null ? "" : titleArr.get(i));}}//遍历数据行for (int i = 0; i < dataList.size(); i++) {Map<String, String> baseFormMap = dataList.get(i);//数据行//fldNameArr字段属性for (int j = 0; j < fldNameArr.size(); j++) {if (sheet.getRow(i + 1) == null) {if (j == 0) {try {sheet.createRow(i + 1).createCell(j).setCellValue(baseFormMap.get(fldNameArr.get(j)) == null ? "" : baseFormMap.get(fldNameArr.get(j)));} catch (Exception e) {if (baseFormMap.get(fldNameArr.get(j)) == null) {sheet.createRow(i + 1).createCell(j).setCellValue("");} else {sheet.createRow(i + 1).createCell(j).setCellValue(baseFormMap.get(fldNameArr.get(j)));}}}} else {BigDecimal b = new BigDecimal(baseFormMap.get(fldNameArr.get(j)));double value = 0d;if (b != null) {value = b.doubleValue();}if (value == 0) {sheet.getRow(i + 1).createCell(j);} else {sheet.getRow(i + 1).createCell(j).setCellValue(b.doubleValue());}}}}// 更新嵌入的workbookPOIXMLDocumentPart xlsPart = chart.getRelations().get(0);OutputStream xlsOut = xlsPart.getPackagePart().getOutputStream();try {wb.write(xlsOut);xlsOut.close();} catch (IOException e) {e.printStackTrace();result = false;} finally {if (wb != null) {try {wb.close();} catch (IOException e) {e.printStackTrace();result = false;}}}return result;}/*** 设置表格样式** @param cell* @param fontName* @param fontSize* @param fontBlod* @param alignment* @param vertical* @param fontColor* @param bgColor* @param cellWidth* @param content*/public static void setWordCellSelfStyle(XWPFTableCell cell, String fontName, String fontSize, int fontBlod,String alignment, String vertical, String fontColor,String bgColor, String cellWidth, String content) {//poi对字体大小设置特殊,不支持小数,但对原word字体大小做了乘2处理BigInteger bFontSize = new BigInteger("24");if (fontSize != null && !fontSize.equals("")) {//poi对字体大小设置特殊,不支持小数,但对原word字体大小做了乘2处理BigDecimal fontSizeBD = new BigDecimal(fontSize);fontSizeBD = bd2.multiply(fontSizeBD);fontSizeBD = fontSizeBD.setScale(0, BigDecimal.ROUND_HALF_UP);//这里取整bFontSize = new BigInteger(fontSizeBD.toString());// 字体大小}// 设置单元格宽度cell.setWidth(cellWidth);//=====获取单元格CTTc tc = cell.getCTTc();//====tcPr开始====》》》》CTTcPr tcPr = tc.getTcPr();//获取单元格里的<w:tcPr>if (tcPr == null) {//没有<w:tcPr>,创建tcPr = tc.addNewTcPr();}//  --vjc开始-->>CTVerticalJc vjc = tcPr.getVAlign();//获取<w:tcPr>  的<w:vAlign w:val="center"/>if (vjc == null) {//没有<w:w:vAlign/>,创建vjc = tcPr.addNewVAlign();}//设置单元格对齐方式vjc.setVal(vertical.equals("top") ? STVerticalJc.TOP : vertical.equals("bottom") ? STVerticalJc.BOTTOM : STVerticalJc.CENTER); //垂直对齐CTShd shd = tcPr.getShd();//获取<w:tcPr>里的<w:shd w:val="clear" w:color="auto" w:fill="C00000"/>if (shd == null) {//没有<w:shd>,创建shd = tcPr.addNewShd();}// 设置背景颜色shd.setFill(bgColor.substring(1));//《《《《====tcPr结束====//====p开始====》》》》CTP p = tc.getPList().get(0);//获取单元格里的<w:p w:rsidR="00C36068" w:rsidRPr="00B705A0" w:rsidRDefault="00C36068" w:rsidP="00C36068">//---ppr开始--->>>CTPPr ppr = p.getPPr();//获取<w:p>里的<w:pPr>if (ppr == null) {//没有<w:pPr>,创建ppr = p.addNewPPr();}//  --jc开始-->>CTJc jc = ppr.getJc();//获取<w:pPr>里的<w:jc w:val="left"/>if (jc == null) {//没有<w:jc/>,创建jc = ppr.addNewJc();}//设置单元格对齐方式jc.setVal(alignment.equals("left") ? STJc.LEFT : alignment.equals("right") ? STJc.RIGHT : STJc.CENTER); //水平对齐//  <<--jc结束--//  --pRpr开始-->>CTParaRPr pRpr = ppr.getRPr(); //获取<w:pPr>里的<w:rPr>if (pRpr == null) {//没有<w:rPr>,创建pRpr = ppr.addNewRPr();}CTFonts pfont = pRpr.getRFonts();//获取<w:rPr>里的<w:rFonts w:ascii="宋体" w:eastAsia="宋体" w:hAnsi="宋体"/>if (pfont == null) {//没有<w:rPr>,创建pfont = pRpr.addNewRFonts();}//设置字体pfont.setAscii(fontName);pfont.setEastAsia(fontName);pfont.setHAnsi(fontName);CTOnOff pb = pRpr.getB();//获取<w:rPr>里的<w:b/>if (pb == null) {//没有<w:b/>,创建pb = pRpr.addNewB();}//设置字体是否加粗pb.setVal(fontBlod == 1 ? STOnOff.ON : STOnOff.OFF);CTHpsMeasure psz = pRpr.getSz();//获取<w:rPr>里的<w:sz w:val="32"/>if (psz == null) {//没有<w:sz w:val="32"/>,创建psz = pRpr.addNewSz();}// 设置单元格字体大小psz.setVal(bFontSize);CTHpsMeasure pszCs = pRpr.getSzCs();//获取<w:rPr>里的<w:szCs w:val="32"/>if (pszCs == null) {//没有<w:szCs w:val="32"/>,创建pszCs = pRpr.addNewSzCs();}// 设置单元格字体大小pszCs.setVal(bFontSize);//  <<--pRpr结束--//<<<---ppr结束---//---r开始--->>>List<CTR> rlist = p.getRList(); //获取<w:p>里的<w:r w:rsidRPr="00B705A0">CTR r = null;if (rlist != null && rlist.size() > 0) {//获取第一个<w:r>r = rlist.get(0);} else {//没有<w:r>,创建r = p.addNewR();}//--rpr开始-->>CTRPr rpr = r.getRPr();//获取<w:r w:rsidRPr="00B705A0">里的<w:rPr>if (rpr == null) {//没有<w:rPr>,创建rpr = r.addNewRPr();}//->-CTFonts font = rpr.getRFonts();//获取<w:rPr>里的<w:rFonts w:ascii="宋体" w:eastAsia="宋体" w:hAnsi="宋体" w:hint="eastAsia"/>if (font == null) {//没有<w:rFonts>,创建font = rpr.addNewRFonts();}//设置字体font.setAscii(fontName);font.setEastAsia(fontName);font.setHAnsi(fontName);CTOnOff b = rpr.getB();//获取<w:rPr>里的<w:b/>if (b == null) {//没有<w:b/>,创建b = rpr.addNewB();}//设置字体是否加粗b.setVal(fontBlod == 1 ? STOnOff.ON : STOnOff.OFF);CTColor color = rpr.getColor();//获取<w:rPr>里的<w:color w:val="FFFFFF" w:themeColor="background1"/>if (color == null) {//没有<w:color>,创建color = rpr.addNewColor();}// 设置字体颜色if (content.contains("↓")) {color.setVal("43CD80");} else if (content.contains("↑")) {color.setVal("943634");} else {color.setVal(fontColor.substring(1));}CTHpsMeasure sz = rpr.getSz();if (sz == null) {sz = rpr.addNewSz();}sz.setVal(bFontSize);CTHpsMeasure szCs = rpr.getSzCs();if (szCs == null) {szCs = rpr.addNewSz();}szCs.setVal(bFontSize);//-<-//<<--rpr结束--List<CTText> tlist = r.getTList();CTText t = null;if (tlist != null && tlist.size() > 0) {//获取第一个<w:r>t = tlist.get(0);} else {//没有<w:r>,创建t = r.addNewT();}t.setStringValue(content);//<<<---r结束---}/*** 获取内置表格数据,拿到第一行第一列格子数据* 有时候模板设计太复杂,对于图表不能精准定位,可以通过设置图表表格数据的第一行第一列格子数据来区分,这个数据不影响图表显示,所以用来区分每个图表*/public static String getZeroData(POIXMLDocumentPart poixmlDocumentPart){String text = "";try {XWPFChart chart = (XWPFChart) poixmlDocumentPart;chart.getCTChart();POIXMLDocumentPart xlsPart = chart.getRelations().get(0);InputStream xlsin = xlsPart.getPackagePart().getInputStream();Workbook workbook = new XSSFWorkbook(xlsin);// 获取第一个sheetSheet sheet = workbook.getSheetAt(0);// 第一行Row row = sheet.getRow(0);// 第一列Cell cell = row.getCell(0);cell.setCellType(CellType.STRING);  // 设置一下格子类型为字符串,不然如果是数字或者时间的话,获取很麻烦text = cell.getStringCellValue();   // 获取格子内容System.out.println("(0,0)格子值:" + text);// 关闭流xlsin.close();} catch (Exception e) {e.printStackTrace();}return text;}}

4.直接运行PoiDemoWordTable类中的main方法即可

该示例中,实现了:文本替换、动态添加表格、柱状图、折线图、饼图、柱状+折线组合图,后续有其它的示例,会继续在此基础上扩展。代码都很简单。

5.运行结果

有个问题(已解决):运行代码之后查看生成的word结果,发现可能部分图表显示数据和实际代码中设置的数据不一致,这种情况都是word模板引起的。
比方说在这个图中只显示了一条折线图,但是实际上代码数据中有3个折线图数据;这里我们右键该图表:点击编辑数据,然后会出来一个excel表格,这就是图表内置的数据,打开后我们可以看到一个蓝色的框框:,这个蓝色框框包含的数据,图表才会显示,这里是图表真正的数据源;对于这种情况,需要手动的把所有数据被蓝色框框包住才行,直接鼠标托就行。

对于这个问题,word应该是有办法解决的,也就是图表的数据源能够动态更新,这个有需要的童鞋可以研究下。

2019-12-04:新增:动态刷新内置Excel数据

1:在原有模板的基础上,新加内置Excel表格文件,如图所示:图片看得清哈......

说明:尝试过使用xlsx后缀的文件,但是代码报错了,报错大概意思是:你确定这是个excel文件?

所以改成xls就好了,至于xlsx为什么不行,下来继续琢磨;

2:一个类代码

该类是在原有的demo代码里面写的,只有一个类,和之前的不冲突,但是用的word模板是同一个;

看着不明白的地方,多瞅瞅,欢迎提出,我也是看的官网来着;

/*** 代码参考了poi官网文档的api示例* poi官方api:http://poi.apache.org/components/document/quick-guide-xwpf.html* 官方示例源码:http://svn.apache.org/repos/asf/poi/trunk/src/examples/src/org/apache/poi/xwpf/usermodel/examples/UpdateEmbeddedDoc.java*/
package com.example.demo;import org.apache.poi.openxml4j.opc.PackagePart;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xwpf.usermodel.XWPFDocument;import java.io.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;/*** 更新模板word中的内置Excel数据* 代码参考了poi官网文档的api示例* poi官方api:http://poi.apache.org/components/document/quick-guide-xwpf.html* 官方示例源码:http://svn.apache.org/repos/asf/poi/trunk/src/examples/src/org/apache/poi/xwpf/usermodel/examples/UpdateEmbeddedDoc.java*/
public class UpdateEmbeddedDoc {private static final String templateurl = "D:\\youxi\\jx\\test.docx";  // 模板文件private static final String returnurl = "D:\\youxi\\jx\\result.docx";  // 结果文件private XWPFDocument doc; // 模板文档对象private File docFile;private static final int SHEET_NUM = 0;   // xls中的sheet页数private static List<Map<String, String>> dataList = new ArrayList<Map<String, String>>();   // 模拟数据static {// 模拟数据for (int i = 0; i < 10; i++) {Map<String, String> map = new HashMap<String, String>();map.put("index-0", "老王吧" + i);map.put("index-1", "老李吧" + i);map.put("index-2", "老张吧" + i);map.put("index-3", "老刘吧" + i);map.put("index-4", "老杨吧" + i);map.put("index-5", "老乌龟吧" + i);dataList.add(map);}}public static void main(String[] args) throws Exception {UpdateEmbeddedDoc ued = new UpdateEmbeddedDoc(templateurl);ued.updateEmbeddedDoc();}/*** 构造函数,加载模板文档* @param filename* @throws IOException*/public UpdateEmbeddedDoc(String filename) throws IOException {this.docFile = new File(filename);if (!this.docFile.exists()) {throw new FileNotFoundException("The Word document " + filename + " does not exist.");}try (FileInputStream fis = new FileInputStream(this.docFile)) {// Open the Word document file and instantiate the XWPFDocument class.this.doc = new XWPFDocument(fis);}}/*** 更新word文档中内置的Excel工作簿* Excel是事先已知的,并且在模板word中已经插入成功了的* @param filename* @throws IOException*/public void updateEmbeddedDoc() throws Exception {// 获取模板文档中的内置部分,也就是获取到插入的Excel文档集合List<PackagePart> embeddedDocs = this.doc.getAllEmbeddedParts();for (PackagePart pPart : embeddedDocs) {// 这里打印出来文档中所有的内置对象,可以看到每个内置对象都有一个唯一标识,通过这个标识可以做相应的业务逻辑;// 这里打印的oleFlag可能因为机器环境原因有所不同,建议先打印出oleFlag的值,对比后再做if判断;// 反正这里输出的内容看一下,就明白了;String oleFlag = pPart.toString();System.out.println(oleFlag);// 记得if中的条件“oleObject1”要和上面的oleFlag对比一下,能够唯一标识就好,我不敢保证每台机器的值都是“oleObject1”// 第一个内置对象,if (oleFlag.indexOf("oleObject1") > 0) {doXls1(pPart);} else if (oleFlag.indexOf("oleObject2") > 0) {doXls2(pPart); // 第二个内置对象,} else if (oleFlag.indexOf("oleObject3") > 0) {doXls3(pPart); // 第三个内置对象,}// TODO.......}if (!embeddedDocs.isEmpty()) {// Finally, write the newly modified Word document out to file.try (FileOutputStream fos = new FileOutputStream(returnurl)) {File file = new File(returnurl);if (file.exists()) {file.delete();}this.doc.write(fos);fos.close();}}}/*** 第一个内置对象,数据的话自己造吧,这里我懒,用了一个数据* @param pPart* @throws Exception*/public void doXls1(PackagePart pPart) throws Exception {try (InputStream is = pPart.getInputStream();Workbook workbook = WorkbookFactory.create(is);OutputStream os = pPart.getOutputStream()) {Sheet sheet = workbook.getSheetAt(SHEET_NUM);int ROW_NUM = 1;// 设置内置xlsx文件的数据,这里示例代码写的很傻瓜,看明白就好了;for (int i = 0; i < dataList.size(); i++) {Row row = sheet.createRow(ROW_NUM); // 创建行Map<String, String> colMap = dataList.get(i);Cell cell0 = row.createCell(0);cell0.setCellValue(colMap.get("index-0")); // 第0列数据Cell cell1 = row.createCell(1);cell1.setCellValue(colMap.get("index-1")); // 第1列数据Cell cell2 = row.createCell(2);cell2.setCellValue(colMap.get("index-2")); // 第2列数据Cell cell3 = row.createCell(3);cell3.setCellValue(colMap.get("index-3")); // 第3列数据Cell cell4 = row.createCell(4);cell4.setCellValue(colMap.get("index-4")); // 第4列数据Cell cell5 = row.createCell(5);cell5.setCellValue(colMap.get("index-5")); // 第5列数据ROW_NUM++;}workbook.write(os);}}/*** 第二个内置对象,数据的话自己造吧,这里我懒,用了一个数据* @param pPart* @throws Exception*/public void doXls2(PackagePart pPart) throws Exception {try (InputStream is = pPart.getInputStream();Workbook workbook = WorkbookFactory.create(is);OutputStream os = pPart.getOutputStream()) {Sheet sheet = workbook.getSheetAt(SHEET_NUM);int ROW_NUM = 1;// 设置内置xlsx文件的数据,这里示例代码写的很傻瓜,看明白就好了;for (int i = 0; i < dataList.size(); i++) {Row row = sheet.createRow(ROW_NUM); // 创建行Map<String, String> colMap = dataList.get(i);Cell cell0 = row.createCell(0);cell0.setCellValue(colMap.get("index-0")); // 第0列数据Cell cell1 = row.createCell(1);cell1.setCellValue(colMap.get("index-1")); // 第1列数据Cell cell2 = row.createCell(2);cell2.setCellValue(colMap.get("index-2")); // 第2列数据Cell cell3 = row.createCell(3);cell3.setCellValue(colMap.get("index-3")); // 第3列数据Cell cell4 = row.createCell(4);cell4.setCellValue(colMap.get("index-4")); // 第4列数据Cell cell5 = row.createCell(5);cell5.setCellValue(colMap.get("index-5")); // 第5列数据ROW_NUM++;}workbook.write(os);}}/*** 第三个内置对象,数据的话自己造吧,这里我懒,用了一个数据* @param pPart* @throws Exception*/public void doXls3(PackagePart pPart) throws Exception {try (InputStream is = pPart.getInputStream();Workbook workbook = WorkbookFactory.create(is);OutputStream os = pPart.getOutputStream()) {Sheet sheet = workbook.getSheetAt(SHEET_NUM);int ROW_NUM = 1;// 设置内置xlsx文件的数据,这里示例代码写的很傻瓜,看明白就好了;for (int i = 0; i < dataList.size(); i++) {Row row = sheet.createRow(ROW_NUM); // 创建行Map<String, String> colMap = dataList.get(i);Cell cell0 = row.createCell(0);cell0.setCellValue(colMap.get("index-0")); // 第0列数据Cell cell1 = row.createCell(1);cell1.setCellValue(colMap.get("index-1")); // 第1列数据Cell cell2 = row.createCell(2);cell2.setCellValue(colMap.get("index-2")); // 第2列数据Cell cell3 = row.createCell(3);cell3.setCellValue(colMap.get("index-3")); // 第3列数据Cell cell4 = row.createCell(4);cell4.setCellValue(colMap.get("index-4")); // 第4列数据Cell cell5 = row.createCell(5);cell5.setCellValue(colMap.get("index-5")); // 第5列数据ROW_NUM++;}workbook.write(os);}}}

生成结果:双击结果文档中的内置excel表格,结果如图

2020-04-07:新增:更新插入的文本框内容

1:在博友的帮助下,添加了对文本框内容的替换,方式很简单,模板截图如下:随意位置插入文本框即可

2:主要类:
该方法参考地址:https://blog.csdn.net/yousun4688/article/details/90108335(感谢老哥)

package com.example.demo.text;import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
import org.apache.xmlbeans.XmlCursor;
import org.apache.xmlbeans.XmlObject;import java.io.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;/*** 对文本框的替换,对文本框的替换,不是正常输入的文本*/
public class TextBox {public static List<String> patternList = new ArrayList();//需要处理的节点名称static {patternList.add("mc:AlternateContent");patternList.add("mc:Choice");patternList.add("w:drawing");patternList.add("wp:anchor");patternList.add("a:graphic");patternList.add("a:graphicData");patternList.add("wps:wsp");patternList.add("wps:txbx");patternList.add("w:txbxContent");patternList.add("w:p");patternList.add("w:r");patternList.add("w:t");}public static void main(String[] args) throws Exception {final String returnurl = "D:\\youxi\\jx\\textbox.docx";  // 结果文件final String templateurl = "D:\\直真科技工作相关\\demo\\src\\main\\resources\\textbox.docx";  // 模板文件InputStream is = new FileInputStream(new File(templateurl));XWPFDocument doc = new XWPFDocument(is);// 替换word模板数据replaceAll(doc);// 保存结果文件try {File file = new File(returnurl);if (file.exists()) {file.delete();}FileOutputStream fos = new FileOutputStream(returnurl);doc.write(fos);fos.close();} catch (Exception e) {e.printStackTrace();}}/*** @Description: 替换段落和表格中*/public static void replaceAll(XWPFDocument doc) throws Exception {doParagraphs(doc); // 处理段落文字数据,包括文字和表格、图片}/*** 处理段落文字** @param doc* @throws InvalidFormatException* @throws FileNotFoundException* @throws IOException*/public static void doParagraphs(XWPFDocument doc) throws Exception {// 文本数据Map<String, String> textMap = new HashMap<String, String>();textMap.put("texttext", "我是被替换的普通文本内容");textMap.put("name", "我是被替换的文本框内容");textMap.put("zuoyou", "左右");changeTextBox(doc, textMap);}public static void changeTextBox(XWPFDocument document, Map<String, String> map) {for (XWPFParagraph paragraph : document.getParagraphs())for (XmlObject object : paragraph.getCTP().getRArray()) {XmlCursor cursor = object.newCursor();eachchild(cursor, 0, map);}}public static void eachchild(XmlCursor cursor, int start, Map<String, String> map) {for (int i = 0; i < 10; i++) {if (cursor.toChild(i)) {if (cursor.getDomNode().getNodeName().equals(patternList.get(start))) {if (start == patternList.size() - 1) {String reString = cursor.getTextValue();System.out.println(reString);reString = reString.replaceAll("\\{\\{", "").replaceAll("}}", "");for (String e : map.keySet()) {if (reString.equals(e)) {//    执行替换reString = reString.replaceAll(e, map.get(e));}}cursor.setTextValue(reString);}eachchild(cursor, start + 1, map);} else {cursor.toParent();}}}cursor.toParent();}}

3:替换后的效果,这个方法对普通文本不会生效,只会对文本框有效,并且替换的逻辑可以自己控制

2020-04-10:新增:新增一个定位图表的方式

问题描述:

在使用word模板中,如果存在很多个图表,如何获取到正确的图表,按照我现有的方式是如图这样。

通过把图表对象toString后获得“/word/charts/chart1.xml”这样的key值,这种方式对于图表在word模板中的位置是从上到下的很好用。

但是如果现在word模板中的图表位置很乱,位置是随意插入的,有横着排,也有竖着排,布局很乱的情况,这种方式就不好用了。

1:解决方案

对图表编辑数据的表格进行了观察,发现第一行第一列数据,无论怎样设置都不会推图表显示有任何影响,如下图所示的这个格子数据。所以在word模板中,修改每个图表的这个格子数据,保证全局唯一,相当于图表的唯一标识。然后在读取word模板时,读取一下图表的数据,获取第一行第一列格子的值,用来当作key。

2:修改模板

右键图表,选择编辑数据,出来的表格,修改第一行第一列的数据,要求全局唯一不重复。然后保存。

3:新增处理方法

该方法拿到POIXMLDocumentPart 对象,拿到Workbook对象,返回的text就是第一行第一列的数据。

/*** 获取内置表格数据,拿到第一行第一列格子数据* 有时候模板设计太复杂,对于图表不能精准定位,可以通过设置图表表格数据的第一行第一列格子数据来区分,这个数据不影响图表显示,所以用来区分每个图表*/public static String getZeroData(POIXMLDocumentPart poixmlDocumentPart){String text = "";try {XWPFChart chart = (XWPFChart) poixmlDocumentPart;chart.getCTChart();POIXMLDocumentPart xlsPart = chart.getRelations().get(0);InputStream xlsin = xlsPart.getPackagePart().getInputStream();Workbook workbook = new XSSFWorkbook(xlsin);// 获取第一个sheetSheet sheet = workbook.getSheetAt(0);// 第一行Row row = sheet.getRow(0);// 第一列Cell cell = row.getCell(0);cell.setCellType(CellType.STRING);  // 设置一下格子类型为字符串,不然如果是数字或者时间的话,获取很麻烦text = cell.getStringCellValue();   // 获取格子内容System.out.println("(0,0)格子值:" + text);// 关闭流xlsin.close();} catch (Exception e) {e.printStackTrace();}return text;}

4:设置key

5:通过key获取图表对象

 

6:代码对应类:com.example.demo.example.barchart.BarChart

2020-04-11:新增:柱状图动态列,解决需要对图表编辑数据才能有效显示的问题

1:主类

package com.example.demo.example.barchart;import com.example.demo.util.PoiWordTools;
import com.example.demo.util.PoiWordToolsDynamic;
import org.apache.poi.ooxml.POIXMLDocumentPart;
import org.apache.poi.xwpf.usermodel.XWPFChart;
import org.apache.poi.xwpf.usermodel.XWPFDocument;import java.io.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;/*** 动态的柱状图,也就是列不确定,由数据决定* 示例代码仅提供实现的参考,可根据自己的业务修改逻辑*/
public class BarChartDynamic {public static void main(String[] args) throws Exception {final String returnurl = "D:\\youxi\\jx\\barchartdynamicresult.docx";  // 结果文件final String templateurl = "D:\\直真科技工作相关\\demo\\src\\main\\resources\\barchartdynamic.docx";  // 模板文件InputStream is = new FileInputStream(new File(templateurl));XWPFDocument doc = new XWPFDocument(is);// 替换word模板数据replaceAll(doc);// 保存结果文件try {File file = new File(returnurl);if (file.exists()) {file.delete();}FileOutputStream fos = new FileOutputStream(returnurl);doc.write(fos);fos.close();} catch (Exception e) {e.printStackTrace();}}/*** @Description: 替换段落和表格和图表等内容*/public static void replaceAll(XWPFDocument doc) throws Exception {doCharts(doc);  // 处理图表数据,柱状图}/*** 处理图表** @param doc* @throws FileNotFoundException*/public static void doCharts(XWPFDocument doc) throws FileNotFoundException {/**----------------------------处理图表------------------------------------**/// 获取word模板中的所有图表元素,用map存放// 为什么不用list保存:查看doc.getRelations()的源码可知,源码中使用了hashMap读取文档图表元素,// 对relations变量进行打印后发现,图表顺序和文档中的顺序不一致,也就是说relations的图表顺序不是文档中从上到下的顺序Map<String, POIXMLDocumentPart> chartsMap = new HashMap<String, POIXMLDocumentPart>();//动态刷新图表List<POIXMLDocumentPart> relations = doc.getRelations();for (POIXMLDocumentPart poixmlDocumentPart : relations) {if (poixmlDocumentPart instanceof XWPFChart) {  // 如果是图表元素// 获取图表对应的表格数据里面的第一行第一列数据,可以拿来当作key值String key = new PoiWordTools().getZeroData(poixmlDocumentPart).trim();System.out.println("key:" + key);chartsMap.put(key, poixmlDocumentPart);}}System.out.println("\n图表数量:" + chartsMap.size() + "\n");// 第一个图表-柱状图doCharts1(chartsMap);}/*** 封装图表数据* @param chartsMap*/public static void doCharts1(Map<String, POIXMLDocumentPart> chartsMap) {// 数据准备List<String> titleArr = new ArrayList<String>();// 标题,也就是对图表选择编辑数据后显示的表格数据的第一行titleArr.add("");titleArr.add("存款$");titleArr.add("欠款$");titleArr.add("还款$");List<String> fldNameArr = new ArrayList<String>();// 字段名(数据有多少列,就多少个)fldNameArr.add("item1");fldNameArr.add("item2");fldNameArr.add("item3");fldNameArr.add("item4");// 数据集合List<Map<String, String>> listItemsByType = new ArrayList<Map<String, String>>();// 数据的话随便整都行// 第一行数据Map<String, String> base1 = new HashMap<String, String>();base1.put("item1", "2020-05");base1.put("item2", (int)(1 + Math.random() * (100 - 1 + 1)) + "");base1.put("item3", (int)(1 + Math.random() * (100 - 1 + 1)) + "");base1.put("item4", (int)(1 + Math.random() * (100 - 1 + 1)) + "");// 第二行数据Map<String, String> base2 = new HashMap<String, String>();base2.put("item1", "2020-06");base2.put("item2", (int)(1 + Math.random() * (100 - 1 + 1)) + "");base2.put("item3", (int)(1 + Math.random() * (100 - 1 + 1)) + "");base2.put("item4", (int)(1 + Math.random() * (100 - 1 + 1)) + "");// 第三行数据Map<String, String> base3 = new HashMap<String, String>();base3.put("item1", "2020-07");base3.put("item2", (int)(1 + Math.random() * (100 - 1 + 1)) + "");base3.put("item3", (int)(1 + Math.random() * (100 - 1 + 1)) + "");base3.put("item4", (int)(1 + Math.random() * (100 - 1 + 1)) + "");listItemsByType.add(base1);listItemsByType.add(base2);listItemsByType.add(base3);// 注意这里的key值POIXMLDocumentPart poixmlDocumentPart = chartsMap.get("嘻嘻嘻嘻");  // 图表对象new PoiWordToolsDynamic().replaceBarCharts(poixmlDocumentPart, titleArr, fldNameArr, listItemsByType);}}

2:辅助类1

package com.example.demo.util;import org.apache.poi.ooxml.POIXMLDocumentPart;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.apache.poi.xwpf.usermodel.XWPFChart;
import org.apache.poi.xwpf.usermodel.XWPFTableCell;
import org.openxmlformats.schemas.drawingml.x2006.chart.*;
import org.openxmlformats.schemas.drawingml.x2006.main.CTSchemeColor;
import org.openxmlformats.schemas.drawingml.x2006.main.STSchemeColorVal;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.*;import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.List;
import java.util.Map;/*** poi生成word的工具类* 可以动态生成图表,可以自己设置图表列的数量*/
public class PoiWordToolsDynamic {private static final BigDecimal bd2 = new BigDecimal("2");/*** 调用替换柱状图数据-可以实现动态列* 其它的折线图、饼图大同小异*/public static void replaceBarCharts(POIXMLDocumentPart poixmlDocumentPart,List<String> titleArr,List<String> fldNameArr,List<Map<String, String>> listItemsByType) {// 很重要的参数,图表系列的数量,由这里的数据决定int culomnNum = titleArr.size() - 1;XWPFChart chart = (XWPFChart) poixmlDocumentPart;chart.getCTChart();//根据属性第一列名称切换数据类型CTChart ctChart = chart.getCTChart();CTPlotArea plotArea = ctChart.getPlotArea();// 柱状图用plotArea.getBarChartArray,// 折线图用plotArea.getLineChartArray,// 饼图用plotArea.getPieChartArray.....// 还有很多其它的CTBarChart barChart = plotArea.getBarChartArray(0);// 清除图表的样式,由代码自己设置barChart.getSerList().clear();//刷新内置excel数据new PoiWordToolsDynamic().refreshExcel(chart, listItemsByType, fldNameArr, titleArr);//刷新页面显示数据-也就是刷新图表的数据源范围refreshBarStrGraphContent(barChart, listItemsByType, fldNameArr, 1, culomnNum, titleArr);}/*** 调用替换柱状图数据-可以实现动态列* 其它的折线图、饼图大同小异*/public static void replaceLineCharts(POIXMLDocumentPart poixmlDocumentPart,List<String> titleArr,List<String> fldNameArr,List<Map<String, String>> listItemsByType) {// 很重要的参数,图表系列的数量,由这里的数据决定int culomnNum = titleArr.size() - 1;XWPFChart chart = (XWPFChart) poixmlDocumentPart;chart.getCTChart();//根据属性第一列名称切换数据类型CTChart ctChart = chart.getCTChart();CTPlotArea plotArea = ctChart.getPlotArea();// 柱状图用plotArea.getBarChartArray,// 折线图用plotArea.getLineChartArray,// 饼图用plotArea.getPieChartArray.....// 还有很多其它的CTLineChart ctLineChart = plotArea.getLineChartArray(0);// 清除图表的样式,由代码自己设置ctLineChart.getSerList().clear();//刷新内置excel数据new PoiWordToolsDynamic().refreshExcel(chart, listItemsByType, fldNameArr, titleArr);//刷新页面显示数据-也就是刷新图表的数据源范围refreshLineStrGraphContent(ctLineChart, listItemsByType, fldNameArr, 1, culomnNum, titleArr);}/*** 动态添加列-柱状图的*/public static boolean refreshBarStrGraphContent(CTBarChart barChart,List<Map<String, String>> dataList,List<String> fldNameArr,int position,int culomnNum,List<String> titleArr) {boolean result = true;//更新数据区域for (int i = 0; i < culomnNum; i++) {CTBarSer ctBarSer = barChart.addNewSer();ctBarSer.addNewIdx().setVal(i);ctBarSer.addNewOrder().setVal(i);// 设置柱状图的系列名称// 设置标题 用以下这个方式,可以兼容office和wps(因为是动态添加,不可以直接get到,需要这样写)CTSerTx tx = ctBarSer.addNewTx();CTStrRef ctStrRef = tx.addNewStrRef();CTStrData ctStrData = ctStrRef.addNewStrCache();ctStrData.addNewPtCount().setVal(1);CTStrVal ctStrVal = ctStrData.addNewPt();ctStrVal.setIdx(0);ctStrVal.setV(titleArr.get(i + 1));  // 设置系列的名称// 设置柱状图系列的颜色,就是显示的柱子的颜色,不设置的话会默认都是黄色// 必须使用ACCENT_x系列的才行CTSchemeColor ctSchemeColor = ctBarSer.addNewSpPr().addNewSolidFill().addNewSchemeClr();ctSchemeColor.setVal(STSchemeColorValTools.get(i));CTAxDataSource cat = ctBarSer.addNewCat();CTNumDataSource val = ctBarSer.addNewVal();CTStrData strData = cat.addNewStrRef().addNewStrCache();CTNumData numData = val.addNewNumRef().addNewNumCache();strData.setPtArray((CTStrVal[]) null); // unset old axis textnumData.setPtArray((CTNumVal[]) null); // unset old values// set modellong idx = 0;for (int j = 0; j < dataList.size(); j++) {//判断获取的值是否为空String value = "0";if (new BigDecimal(dataList.get(j).get(fldNameArr.get(i + position))) != null) {value = new BigDecimal(dataList.get(j).get(fldNameArr.get(i + position))).toString();}if (!"0".equals(value)) {CTNumVal numVal = numData.addNewPt();//序列值numVal.setIdx(idx);numVal.setV(value);}CTStrVal sVal = strData.addNewPt();//序列名称sVal.setIdx(idx);sVal.setV(dataList.get(j).get(fldNameArr.get(0)));idx++;}numData.addNewPtCount().setVal(idx);strData.addNewPtCount().setVal(idx);}return result;}/*** 动态添加列-折线图的*/public static boolean refreshLineStrGraphContent(CTLineChart ctLineChart,List<Map<String, String>> dataList,List<String> fldNameArr,int position,int culomnNum,List<String> titleArr) {boolean result = true;//更新数据区域for (int i = 0; i < culomnNum; i++) {CTLineSer ctLineSer = ctLineChart.addNewSer();ctLineSer.addNewIdx().setVal(i);ctLineSer.addNewOrder().setVal(i);// 设置柱状图的系列名称// 设置标题 用以下这个方式,可以兼容office和wps(因为是动态添加,不可以直接get到,需要这样写)CTSerTx tx = ctLineSer.addNewTx();CTStrRef ctStrRef = tx.addNewStrRef();CTStrData ctStrData = ctStrRef.addNewStrCache();ctStrData.addNewPtCount().setVal(1);CTStrVal ctStrVal = ctStrData.addNewPt();ctStrVal.setIdx(0);ctStrVal.setV(titleArr.get(i + 1));  // 设置系列的名称// 设置柱状图系列的颜色,就是显示的柱子的颜色,不设置的话会默认都是黄色// 必须使用ACCENT_x系列的才行CTSchemeColor ctSchemeColor = ctLineSer.addNewSpPr().addNewLn().addNewSolidFill().addNewSchemeClr();ctSchemeColor.setVal(STSchemeColorValTools.get(i));CTAxDataSource cat = ctLineSer.addNewCat();CTNumDataSource val = ctLineSer.addNewVal();CTStrData strData = cat.addNewStrRef().addNewStrCache();CTNumData numData = val.addNewNumRef().addNewNumCache();strData.setPtArray((CTStrVal[]) null); // unset old axis textnumData.setPtArray((CTNumVal[]) null); // unset old values// set modellong idx = 0;for (int j = 0; j < dataList.size(); j++) {//判断获取的值是否为空String value = "0";if (new BigDecimal(dataList.get(j).get(fldNameArr.get(i + position))) != null) {value = new BigDecimal(dataList.get(j).get(fldNameArr.get(i + position))).toString();}if (!"0".equals(value)) {CTNumVal numVal = numData.addNewPt();//序列值numVal.setIdx(idx);numVal.setV(value);}CTStrVal sVal = strData.addNewPt();//序列名称sVal.setIdx(idx);sVal.setV(dataList.get(j).get(fldNameArr.get(0)));idx++;}numData.addNewPtCount().setVal(idx);strData.addNewPtCount().setVal(idx);}return result;}/*** 刷新内置excel数据** @param chart* @param dataList* @param fldNameArr* @param titleArr* @return*/public static boolean refreshExcel(XWPFChart chart,List<Map<String, String>> dataList, List<String> fldNameArr, List<String> titleArr) {boolean result = true;Workbook wb = new XSSFWorkbook();Sheet sheet = wb.createSheet("Sheet1");//根据数据创建excel第一行标题行for (int i = 0; i < titleArr.size(); i++) {if (sheet.getRow(0) == null) {sheet.createRow(0).createCell(i).setCellValue(titleArr.get(i) == null ? "" : titleArr.get(i));} else {sheet.getRow(0).createCell(i).setCellValue(titleArr.get(i) == null ? "" : titleArr.get(i));}}//遍历数据行for (int i = 0; i < dataList.size(); i++) {Map<String, String> baseFormMap = dataList.get(i);//数据行//fldNameArr字段属性for (int j = 0; j < fldNameArr.size(); j++) {if (sheet.getRow(i + 1) == null) {if (j == 0) {try {sheet.createRow(i + 1).createCell(j).setCellValue(baseFormMap.get(fldNameArr.get(j)) == null ? "" : baseFormMap.get(fldNameArr.get(j)));} catch (Exception e) {if (baseFormMap.get(fldNameArr.get(j)) == null) {sheet.createRow(i + 1).createCell(j).setCellValue("");} else {sheet.createRow(i + 1).createCell(j).setCellValue(baseFormMap.get(fldNameArr.get(j)));}}}} else {BigDecimal b = new BigDecimal(baseFormMap.get(fldNameArr.get(j)));double value = 0d;if (b != null) {value = b.doubleValue();}if (value == 0) {sheet.getRow(i + 1).createCell(j);} else {sheet.getRow(i + 1).createCell(j).setCellValue(b.doubleValue());}}}}// 更新嵌入的workbookPOIXMLDocumentPart xlsPart = chart.getRelations().get(0);OutputStream xlsOut = xlsPart.getPackagePart().getOutputStream();try {wb.write(xlsOut);xlsOut.close();} catch (IOException e) {e.printStackTrace();result = false;} finally {if (wb != null) {try {wb.close();} catch (IOException e) {e.printStackTrace();result = false;}}}return result;}}

3:辅助类2

package com.example.demo.util;import org.openxmlformats.schemas.drawingml.x2006.main.STSchemeColorVal;import java.util.ArrayList;
import java.util.List;/*** 官方对于颜色的解释: http://poi.apache.org/apidocs/dev/org/apache/poi/sl/draw/binding/STSchemeColorVal.html*/
public final class STSchemeColorValTools {static List<STSchemeColorVal.Enum> colorEnum = new ArrayList<>();static {
//        colorEnum.add(STSchemeColorVal.BG_1);
//        colorEnum.add(STSchemeColorVal.BG_2);
//        colorEnum.add(STSchemeColorVal.TX_1);
//        colorEnum.add(STSchemeColorVal.TX_2);colorEnum.add(STSchemeColorVal.ACCENT_1);colorEnum.add(STSchemeColorVal.ACCENT_2);colorEnum.add(STSchemeColorVal.ACCENT_3);colorEnum.add(STSchemeColorVal.ACCENT_4);colorEnum.add(STSchemeColorVal.ACCENT_5);colorEnum.add(STSchemeColorVal.ACCENT_6);
//        colorEnum.add(STSchemeColorVal.HLINK);
//        colorEnum.add(STSchemeColorVal.FOL_HLINK);
//        colorEnum.add(STSchemeColorVal.PH_CLR);
//        colorEnum.add(STSchemeColorVal.DK_1);
//        colorEnum.add(STSchemeColorVal.DK_2);
//        colorEnum.add(STSchemeColorVal.LT_1);
//        colorEnum.add(STSchemeColorVal.LT_2);}public static STSchemeColorVal.Enum get(int i){return colorEnum.get(i);}
}

4:模板

5:结果

6:如果要显示数字的话,直接在模板里面设置显示就好了

2020-04-21:新增:表格进行合并单元格,横竖都行,两种方式(感谢刘老师)

场景:在word中插入表格,并对表格进行合并,这里提供两种方式,分别对应两个类

1:模板文档内容:

2:类1

package com.example.demo.example.table;import com.example.demo.util.PoiWordTools;
import org.apache.poi.ooxml.POIXMLDocumentPart;
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.util.Units;
import org.apache.poi.xwpf.usermodel.*;
import org.apache.xmlbeans.XmlCursor;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTHMerge;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTVMerge;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.STMerge;
import org.springframework.util.StringUtils;import java.io.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;/*** 表格*/
public class Table {public static void main(String[] args) throws Exception {final String returnurl = "D:\\youxi\\jx\\table1.docx";  // 结果文件final String templateurl = "D:\\POI\\demo\\src\\main\\resources\\table1.docx";  // 模板文件InputStream is = new FileInputStream(new File(templateurl));XWPFDocument doc = new XWPFDocument(is);// 替换word模板数据replaceAll(doc);// 保存结果文件try {File file = new File(returnurl);if (file.exists()) {file.delete();}FileOutputStream fos = new FileOutputStream(returnurl);doc.write(fos);fos.close();} catch (Exception e) {e.printStackTrace();}}/*** @Description: 替换段落和表格中*/public static void replaceAll(XWPFDocument doc) throws Exception {doParagraphs(doc); // 处理段落文字数据,包括文字和表格、图片}/*** 处理段落文字** @param doc* @throws InvalidFormatException* @throws FileNotFoundException* @throws IOException*/public static void doParagraphs(XWPFDocument doc) throws Exception {/**----------------------------处理段落------------------------------------**/List<XWPFParagraph> paragraphList = doc.getParagraphs();if (paragraphList != null && paragraphList.size() > 0) {for (XWPFParagraph paragraph : paragraphList) {List<XWPFRun> runs = paragraph.getRuns();for (XWPFRun run : runs) {String text = run.getText(0);if (text != null) {// 动态表格if (text.contains("${table1}")) {run.setText("", 0);XmlCursor cursor = paragraph.getCTP().newCursor();XWPFTable tableOne = doc.insertNewTbl(cursor);// ---这个是关键// 设置表格宽度,第一行宽度就可以了,这个值的单位,目前我也还不清楚,还没来得及研究tableOne.setWidth(8500);// 表格第一行,对于每个列,必须使用createCell(),而不是getCell(),因为第一行嘛,肯定是属于创建的,没有create哪里来的get呢XWPFTableRow tableOneRowOne = tableOne.getRow(0);//行new PoiWordTools().setWordCellSelfStyle(tableOneRowOne.getCell(0), "微软雅黑", "9", 0, "left", "top", "#000000", "#B4C6E7", "10%", "序号");new PoiWordTools().setWordCellSelfStyle(tableOneRowOne.createCell(), "微软雅黑", "9", 0, "left", "top", "#000000", "#B4C6E7", "45%", "公司名称(英文)");new PoiWordTools().setWordCellSelfStyle(tableOneRowOne.createCell(), "微软雅黑", "9", 0, "left", "top", "#000000", "#B4C6E7", "45%", "公司名称(中文)");for (int i = 0; i < 10; i ++) {// 表格第二行XWPFTableRow tableOneRowTwo = tableOne.createRow();//行new PoiWordTools().setWordCellSelfStyle(tableOneRowTwo.getCell(0), "微软雅黑", "9", 0, "left", "top", "#000000", "#ffffff", "10%", "一行一列");new PoiWordTools().setWordCellSelfStyle(tableOneRowTwo.getCell(1), "微软雅黑", "9", 0, "left", "top", "#000000", "#ffffff", "45%", "一行一列");new PoiWordTools().setWordCellSelfStyle(tableOneRowTwo.getCell(2), "微软雅黑", "9", 0, "left", "top", "#000000", "#ffffff", "45%", "一行一列");}// 横着合并单元格  -----------------------------CTHMerge cthMergeStart = CTHMerge.Factory.newInstance();cthMergeStart.setVal(STMerge.RESTART);CTHMerge cthMergeEnd = CTHMerge.Factory.newInstance();cthMergeEnd.setVal(STMerge.CONTINUE);XWPFTableCell cell71 = tableOne.getRow(6).getCell(0);  // 第7行的第1列XWPFTableCell cell72 = tableOne.getRow(6).getCell(1);  // 第7行的第2列XWPFTableCell cell73 = tableOne.getRow(6).getCell(2);  // 第7行的第3列cell71.getCTTc().getTcPr().setHMerge(cthMergeStart);cell72.getCTTc().getTcPr().setHMerge(cthMergeEnd);cell73.getCTTc().getTcPr().setHMerge(cthMergeEnd);// 竖着合并单元格   -----------------------------CTVMerge vmergeStart = CTVMerge.Factory.newInstance();vmergeStart.setVal(STMerge.RESTART);CTVMerge vmergeEnd = CTVMerge.Factory.newInstance();vmergeEnd.setVal(STMerge.CONTINUE);XWPFTableCell cell1 = tableOne.getRow(1).getCell(0);  // 第2行第1列  第1行是表头XWPFTableCell cell2 = tableOne.getRow(2).getCell(0);  // 第3行第1列XWPFTableCell cell3 = tableOne.getRow(3).getCell(0);  // 第4行第1列XWPFTableCell cell4 = tableOne.getRow(4).getCell(0);  // 第5行第1列cell1.getCTTc().getTcPr().setVMerge(vmergeStart);cell2.getCTTc().getTcPr().setVMerge(vmergeEnd);cell3.getCTTc().getTcPr().setVMerge(vmergeEnd);cell4.getCTTc().getTcPr().setVMerge(vmergeEnd);// .......}}}}}}}

3:类2

package com.example.demo.example.table;import com.example.demo.util.PoiWordTools;
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.xwpf.usermodel.*;
import org.apache.xmlbeans.XmlCursor;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTHMerge;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTVMerge;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.STMerge;import java.io.*;
import java.util.List;/*** 表格*/
public class Table2 {public static void main(String[] args) throws Exception {final String returnurl = "D:\\youxi\\jx\\table1.docx";  // 结果文件final String templateurl = "D:\\POI\\demo\\src\\main\\resources\\table2.docx";  // 模板文件InputStream is = new FileInputStream(new File(templateurl));XWPFDocument doc = new XWPFDocument(is);// 替换word模板数据replaceAll(doc);// 保存结果文件try {File file = new File(returnurl);if (file.exists()) {file.delete();}FileOutputStream fos = new FileOutputStream(returnurl);doc.write(fos);fos.close();} catch (Exception e) {e.printStackTrace();}}/*** @Description: 替换段落和表格中*/public static void replaceAll(XWPFDocument doc) throws Exception {doParagraphs(doc); // 处理段落文字数据,包括文字和表格、图片}/*** 处理段落文字** @param doc* @throws InvalidFormatException* @throws FileNotFoundException* @throws IOException*/public static void doParagraphs(XWPFDocument doc) throws Exception {/**----------------------------处理段落------------------------------------**/List<XWPFParagraph> paragraphList = doc.getParagraphs();if (paragraphList != null && paragraphList.size() > 0) {for (XWPFParagraph paragraph : paragraphList) {List<XWPFRun> runs = paragraph.getRuns();for (XWPFRun run : runs) {String text = run.getText(0);if (text != null) {// 动态表格if (text.contains("${table1}")) {run.setText("", 0);XmlCursor cursor = paragraph.getCTP().newCursor();XWPFTable tableOne = doc.insertNewTbl(cursor);// ---这个是关键// 设置表格宽度,第一行宽度就可以了,这个值的单位,目前我也还不清楚,还没来得及研究tableOne.setWidth(8500);// 表格第一行,对于每个列,必须使用createCell(),而不是getCell(),因为第一行嘛,肯定是属于创建的,没有create哪里来的get呢XWPFTableRow tableOneRowOne = tableOne.getRow(0);//行new PoiWordTools().setWordCellSelfStyle(tableOneRowOne.getCell(0), "微软雅黑", "9", 0, "left", "top", "#000000", "#B4C6E7", "10%", "序号");new PoiWordTools().setWordCellSelfStyle(tableOneRowOne.createCell(), "微软雅黑", "9", 0, "left", "top", "#000000", "#B4C6E7", "45%", "公司名称(英文)");new PoiWordTools().setWordCellSelfStyle(tableOneRowOne.createCell(), "微软雅黑", "9", 0, "left", "top", "#000000", "#B4C6E7", "45%", "公司名称(中文)");for (int i = 0; i < 10; i ++) {// 表格第二行XWPFTableRow tableOneRowTwo = tableOne.createRow();//行new PoiWordTools().setWordCellSelfStyle(tableOneRowTwo.getCell(0), "微软雅黑", "9", 0, "left", "top", "#000000", "#ffffff", "10%", "一行一列");new PoiWordTools().setWordCellSelfStyle(tableOneRowTwo.getCell(1), "微软雅黑", "9", 0, "left", "top", "#000000", "#ffffff", "45%", "一行一列");new PoiWordTools().setWordCellSelfStyle(tableOneRowTwo.getCell(2), "微软雅黑", "9", 0, "left", "top", "#000000", "#ffffff", "45%", "一行一列");}// 横着合并单元格  -----------------------------XWPFTableCell cell71 = tableOne.getRow(6).getCell(0);  // 第7行的第1列XWPFTableCell cell72 = tableOne.getRow(6).getCell(1);  // 第7行的第2列XWPFTableCell cell73 = tableOne.getRow(6).getCell(2);  // 第7行的第3列cell71.getCTTc().addNewTcPr().addNewHMerge().setVal(STMerge.RESTART);cell72.getCTTc().addNewTcPr().addNewHMerge().setVal(STMerge.CONTINUE);cell73.getCTTc().addNewTcPr().addNewHMerge().setVal(STMerge.CONTINUE);// 竖着合并单元格  -----------------------------XWPFTableCell cell1 = tableOne.getRow(1).getCell(0);  // 第2行第1列  第1行是表头XWPFTableCell cell2 = tableOne.getRow(2).getCell(0);  // 第3行第1列XWPFTableCell cell3 = tableOne.getRow(3).getCell(0);  // 第4行第1列XWPFTableCell cell4 = tableOne.getRow(4).getCell(0);  // 第5行第1列cell1.getCTTc().addNewTcPr().addNewVMerge().setVal(STMerge.RESTART);cell2.getCTTc().addNewTcPr().addNewVMerge().setVal(STMerge.CONTINUE);cell3.getCTTc().addNewTcPr().addNewVMerge().setVal(STMerge.CONTINUE);cell4.getCTTc().addNewTcPr().addNewVMerge().setVal(STMerge.CONTINUE);// .......}}}}}}}

4:结果:

两种方式本质上是一样的!

2020-04-23:解决问题:组合图页面显示的数据,跟第二列对应不上(感谢博友:吹古拉朽)

事情是这样的,博客网友吹古拉朽说,组合图页面显示的数据,跟第二列对应不上,柱状图+折线图的图表,折线图会显示柱状图的数据,显示结果如下图:很明显是不对的;

如果你已经运行过我的示例代码中的PoiDemoWordTable.java这个类,生成的第6个图表,就是这个;

解决方案:在第六个图表对应的代码中,修改position参数的值,如图:这个参数代表数据下标,从0开始的。

所以我这里改成3就好了。

本文示例源代码:(码云地址)https://gitee.com/loverer/poi-demo

java使用poi在word中生成柱状图、折线图、饼图、柱状图+折线图组合图、动态表格、文本替换、图片替换、更新内置Excel数据、更新插入的文本框内容、合并表格单元格;相关推荐

  1. java使用poi在word中生成柱状图、折线图、饼图、柱状图+折线图组合图、动态表格、文本替换、图片替换 springboot项目

    本文参考地址:https://blog.csdn.net/u014427811/article/details/100771314 在参考文章的基础上,增加了模板样例 模板样例地址 百度网盘 链接:h ...

  2. 使用java Apache poi 根据word模板生成word报表

    使用java Apache poi 根据word模板生成word报表 使用poi读取word模板,替换word中的{text}标签,并根据自定义标签循环生成表格或表格中的行. 代码示例下载:https ...

  3. java使用poi读写word中的图片(二)

    文章目录 准备工作 简单读取 复杂读取 查看Word的XML 特别说明:Word中的Svg图片 第一种写入图片到Word中的方式 第二种写入图片到Word中的方式 最后 准备工作 这里就不在复述了,可 ...

  4. 【Apache Poi】如何使用poi在word中生成复选框

    如何使用poi在word中生成复选框 应用场景 解决方式 代码示例 结语 应用场景 我们经常会在开发中遇到需要通过Poi来生成类似下面这样的复选框 解决方式 我们可以通过unicode编码:\u25A ...

  5. apache poi使用例_使用java Apache poi 根据word模板生成word报表例子

    [实例简介] 使用java Apache poi 根据word模板生成word报表 仅支持docx格式的word文件,大概是word2010及以后版本,doc格式不支持. 使用说明:https://b ...

  6. java利用POI在word中绘制折线图

    1.poi的简介 Apache POI 简介是用Java编写的免费开源的跨平台的 Java API,Apache POI提供API给Java程式对Microsoft Office(Excel.WORD ...

  7. poi编辑word中的chart,对图表数据替换,并刷新图表

    最近工作需要做报表导出,再次接触poi,上次用poi的时候还是上次(在前司编辑excel的时候). 而word呢,数据结构上感觉又比excel复杂,至少我处理的时候认为是这样. 首先看一下要处理的wo ...

  8. 记录java使用POI实现word模板数据填充

    一.前言 最近项目遇到个需求,在模板中填充数据,刚开始是用smartBi报表进行填充,输出参数都是必录,无法满足需求.在网上查了很多资料,有些需要把文档转成xml格式修改里面内容,IDEA里面修改要替 ...

  9. excel中如何使内容不要超出单元格

    https://jingyan.baidu.com/article/cd4c2979bf38bb356f6e6006.html 以上是原文链接 摘要: 首先,在桌面点击鼠标右键,点击"新建& ...

最新文章

  1. javascript面向对象学习笔记(一)——继承
  2. 以ActiveMQ为例JAVA消息中间件学习【4】——消息中间件实际应用场景
  3. 本周 Github 精选:13 款炼丹利器,有开源工具包也有超大数据集
  4. linux服务器之间文件复制命令
  5. MFC窗口程序显示命令行输出窗口的方法
  6. excel怎么批量插行_批量制作anki卡片最易上手方法
  7. 【Git入门之二】基本术语
  8. 第二阶段团队站立会议08
  9. JavaScript 拓扑排序 算法
  10. html 在行前显示黑点,word 每行前有个黑点 word 每行前面的黑点
  11. Rhino 6 Essential Training Rhino6基本教程 Lynda课程中文字幕
  12. python 北上资金_北上资金持股相关因子
  13. 域名解析ip地址的过程
  14. RabbitMQ中的cluster、mirrored queue,以及 warrens机制、RAM node、disk node及vhost介绍
  15. 使用共享收款码系统,打造微商模式经营门店是关键
  16. 自己解决个税申报的步骤
  17. 人身三流指什么_关于《三流人生》那些事儿
  18. 有知群产品经理必修TOP班学习笔记
  19. 虚拟机(VMware)配置静态IP
  20. Cocos Creator AssetBundle 游戏分包方案评估

热门文章

  1. 中国电子学会2021年06月份青少年软件编程Python等级考试试卷三级真题(含答案)
  2. Cadence OrCAD原理图如何统计元件总数量
  3. java连接mppdb数据库,高斯数据库 (gaussDB) - 基于 JDBC 开发 (4)
  4. 【用户登录】网关,token,全局异常处理
  5. 计算机ping不通格式,如何解决 ping 不通 127.0.0.1
  6. BlueStack与真机的运行相同代码,效果不同的解决
  7. Ueditor集成Word导入(富文本编辑器)
  8. 前端必看书籍总结(哪些值得看)
  9. 数字电网的定义、内涵和特征
  10. Eureka如何实现自我保护机制