使用Apache POI更新Word文档中的Excel图表不生效问题
Apache POI-3.17更新Word图表后未同步显示的问题解决
我在Java中使用Apache POI-3.17更新Word文档中的图表,已成功修改图表关联的Excel底层数据,但打开Word文档后图表并未同步更新,仅在点击图表选择“编辑数据”时才会显示最新数值。
原始代码
import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.nio.file.Files; import java.nio.file.Paths; import java.util.Iterator; import java.util.List; import org.apache.commons.lang3.StringUtils; import org.apache.poi.POIXMLDocumentPart; import org.apache.poi.POIXMLDocumentPart.RelationPart; import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.Row; import org.apache.poi.xssf.usermodel.XSSFSheet; import org.apache.poi.xssf.usermodel.XSSFWorkbook; import org.apache.poi.xwpf.converter.pdf.PdfConverter; import org.apache.poi.xwpf.converter.pdf.PdfOptions; import org.apache.poi.xwpf.usermodel.XWPFDocument; import org.apache.poi.xwpf.usermodel.XWPFParagraph; public class SherlockApp { public static void main(String[] args) { String fileName = "D:\\Circular Economy Report Template.docx"; try (XWPFDocument doc = new XWPFDocument(Files.newInputStream(Paths.get(fileName)))) { List<XWPFParagraph> list = doc.getParagraphs(); for (XWPFParagraph paragraph : list) { // System.out.println(paragraph.getText()); } for (POIXMLDocumentPart part : doc.getRelations()) { if (!StringUtils.equalsIgnoreCase(part.getPackagePart().getContentTypeDetails().getSubType(), "vnd.openxmlformats-officedocument.drawingml.chart+xml")) { continue; } for(RelationPart p : part.getRelationParts()) { System.out.println("Test -> "+p.getDocumentPart()); InputStream str = p.getDocumentPart().getPackagePart().getInputStream(); XSSFWorkbook book = new XSSFWorkbook(str); XSSFSheet sheet = book.getSheetAt(0); Iterator<Row> itr = sheet.iterator(); while (itr.hasNext()) { Row row = itr.next(); Iterator<Cell> cellIterator = row.cellIterator(); while (cellIterator.hasNext()) { Cell cell = cellIterator.next(); switch (cell.getCellType()) { case Cell.CELL_TYPE_STRING: System.out.print(cell.getStringCellValue() + "\t\t\t"); break; case Cell.CELL_TYPE_NUMERIC: System.out.print(cell.getNumericCellValue() + "\t\t\t"); break; default: } } System.out.println(""); } // 更新数据 Cell cell = sheet.getRow(1).getCell(2); cell.setCellValue(0.1); cell = sheet.getRow(1).getCell(1); cell.setCellValue(0.9); System.out.println("Cell Value -> "+cell.getNumericCellValue()); str.close(); OutputStream oStr = p.getDocumentPart().getPackagePart().getOutputStream(); book.write(oStr); book.close(); // 原代码在循环内多次保存文档,建议移到循环外 try (FileOutputStream out = new FileOutputStream(fileName)) { doc.write(out); } } } } catch (Exception e) { e.printStackTrace(); } } }
问题原因
Word图表采用双重数据存储机制:
- 关联的Excel数据源(已成功修改)
- 图表自身的
chart.xml文件中缓存了一份静态渲染数据(numCache/strCache节点)
Word默认打开时优先使用这份缓存数据渲染图表,仅在触发“编辑数据”操作时才会重新读取Excel数据源并更新缓存,因此出现修改后图表不刷新的情况。
解决方案(POI-3.17适配)
由于POI-3.17对图表高级操作支持有限,需手动解析并修改chart.xml中的缓存数据,步骤如下:
- 获取图表对应的POIXMLDocumentPart,解析其XML内容
- 定位对应数据系列的缓存节点
- 更新缓存中的数值与Excel数据保持一致
- 统一保存文档
补充后的关键代码片段
// 在修改完Excel数据后,添加这段代码更新图表缓存 for (POIXMLDocumentPart part : doc.getRelations()) { if (!StringUtils.equalsIgnoreCase(part.getPackagePart().getContentTypeDetails().getSubType(), "vnd.openxmlformats-officedocument.drawingml.chart+xml")) { continue; } // 获取图表XML结构 org.apache.poi.xssf.usermodel.XSSFChart chart = (org.apache.poi.xssf.usermodel.XSSFChart) part; org.openxmlformats.schemas.drawingml.x2006.chart.CTChart ctChart = chart.getCTChart(); org.openxmlformats.schemas.drawingml.x2006.chart.CTPlotArea plotArea = ctChart.getPlotArea(); // 针对饼图更新缓存(其他图表类型需调整节点路径) for (org.openxmlformats.schemas.drawingml.x2006.chart.CTPieSer pieSer : plotArea.getPieChartList().get(0).getSerList()) { org.openxmlformats.schemas.drawingml.x2006.chart.CTNumData numData = pieSer.getVal().getNumCache(); // 清空旧缓存 numData.getPtList().clear(); // 添加新数值(对应Excel修改后的0.9和0.1) org.openxmlformats.schemas.drawingml.x2006.chart.CTNumVal pt1 = numData.addNewPt(); pt1.setIdx(0); pt1.setV("0.9"); org.openxmlformats.schemas.drawingml.x2006.chart.CTNumVal pt2 = numData.addNewPt(); pt2.setIdx(1); pt2.setV("0.1"); // 更新缓存点数量 numData.getPtCount().setVal(2); } // 保存图表XML修改 chart.write(part.getPackagePart().getOutputStream()); } // 所有修改完成后统一保存文档 try (FileOutputStream out = new FileOutputStream(fileName)) { doc.write(out); }
注意事项
- 不同类型的图表(柱状图、折线图等)对应XML节点结构不同,需根据实际图表类型调整代码
- POI-3.17需要引入
ooxml-schemas依赖才能操作CT开头的XML对象类 - 避免在循环内多次保存文档,防止文件占用或数据不一致
内容的提问来源于stack exchange,提问作者Nitin Rathod
相关产品推荐
相关产品推荐

