You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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中的缓存数据,步骤如下:

  1. 获取图表对应的POIXMLDocumentPart,解析其XML内容
  2. 定位对应数据系列的缓存节点
  3. 更新缓存中的数值与Excel数据保持一致
  4. 统一保存文档

补充后的关键代码片段

// 在修改完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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.10 01:55:15