为什么我不能在Apache POI中将一个工作簿链接到另一个工作簿?

我有一个工作簿,里面有一些数据。 我正在使用该工作簿,并根据其他工作簿中的数据创build另一个工作簿,并在其中包含折线图。 代码运行良好,但每当我打开graphics文件,我得到的警告We can't update some of the links in your workbook right now 。 如果我单击警告菜单中的“ Edit Links...button,将显示无法find数据工作簿。 如果我点击Change Source... ,然后select正确的工作簿,那么工作正常。 为什么是这样? POI能否保留两个文件之间的链接?

我的代码:

创build数据工作簿:

 public static XSSFWorkbook createDataSpreadsheet(String name, long[] data) { XSSFWorkbook workbook = new XSSFWorkbook(); XSSFSheet sheet = workbook.createSheet(name); int rowNumber = 0; for(int i = 1; i < data.length + 1; i++) { Row row = sheet.createRow(rowNumber++); int columnNumber = 0; row.createCell(columnNumber++).setCellValue(i); row.createCell(columnNumber++).setCellValue(data[i - 1]); } return workbook; } 

要创buildgraphics工作簿:

 public static XSSFWorkbook createLineChart(String name, XSSFWorkbook data) { XSSFWorkbook workbook = new XSSFWorkbook(); XSSFSheet sheet = workbook.createSheet(name); XSSFDrawing drawing = sheet.createDrawingPatriarch(); XSSFClientAnchor anchor = drawing.createAnchor(0, 0, 0, 0, 0, 0, 15, 15); XSSFChart lineChart = drawing.createChart(anchor); XSSFChartLegend legend = lineChart.getOrCreateLegend(); legend.setPosition(LegendPosition.BOTTOM); LineChartData chartData = lineChart.getChartDataFactory().createLineChartData(); ChartAxis bottomAxis = lineChart.getChartAxisFactory().createCategoryAxis(AxisPosition.BOTTOM); ValueAxis leftAxis = lineChart.getChartAxisFactory().createValueAxis(AxisPosition.LEFT); leftAxis.setCrosses(AxisCrosses.AUTO_ZERO); XSSFSheet dataSheet = data.getSheetAt(0); ChartDataSource<Number> xData = DataSources.fromNumericCellRange(dataSheet, new CellRangeAddress(0, dataSheet.getLastRowNum(), 0, 0)); ChartDataSource<Number> yData = DataSources.fromNumericCellRange(dataSheet, new CellRangeAddress(0, dataSheet.getLastRowNum(), 1, 1)); LineChartSeries chartSeries = chartData.addSeries(xData, yData); chartSeries.setTitle("A title"); lineChart.plot(chartData, new ChartAxis[] { bottomAxis, leftAxis }); return workbook; } 

XSSF创build外部链接到现在还没有很好的实施。 有ExternalLinksTable,但如果你看看这个类的使用,那么你会看到只提供阅读这些外部链接,而不是创build和写作。

所以我们需要使用低级对象。 而且我们需要了解Office OpenXML *.xlsx ZIP-archive中这个外部链接的内部依赖关系。

以下工作只要两个工作簿都存储在同一个目录中。

代码主要是您提供的代码添加一个方法创build外部链接到另一个工作簿中的工作表。 这种方法使用低级别的对象,并不是非常通用的,但它应该显示的原则。

其他代码也会被注释掉。

 import java.io.*; import org.apache.poi.ss.usermodel.*; import org.apache.poi.ss.usermodel.charts.*; import org.apache.poi.ss.util.CellRangeAddress; import org.apache.poi.xssf.usermodel.*; import org.apache.poi.xssf.model.ExternalLinksTable; import org.apache.poi.openxml4j.opc.*; import org.apache.poi.POIXMLDocumentPart; import org.openxmlformats.schemas.spreadsheetml.x2006.main.ExternalLinkDocument; import static org.apache.poi.POIXMLTypeLoader.DEFAULT_XML_OPTIONS; public class CreateExcelLineChartDataAnotherWorkbook { private static String datawbname = "DataWB.xlsx"; private static String chartwbname = "ChartWB.xlsx"; public CreateExcelLineChartDataAnotherWorkbook() throws Exception { Workbook datawb = createDataSpreadsheet("ChartDataSheet"); saveWorkbook(datawb, "/home/axel/Dokumente/"+datawbname); Workbook chartwb = createLineChart("ChartSheet", (XSSFWorkbook)datawb); saveWorkbook(chartwb, "/home/axel/Dokumente/"+chartwbname); } //your method only partially changed to have sample data public XSSFWorkbook createDataSpreadsheet(String name) { Workbook workbook = new XSSFWorkbook(); Sheet sheet = workbook.createSheet(name); int rowNumber = 0; for(int i = 0; i < 20; i++) { Row row = sheet.createRow(rowNumber++); int columnNumber = 0; row.createCell(columnNumber++).setCellValue(Math.PI*i/10*2); row.createCell(columnNumber++).setCellValue(Math.sin(Math.PI*i/10*2)); } return (XSSFWorkbook)workbook; } //method for saving the workbooks public void saveWorkbook(Workbook wb, String path) throws Exception { wb.write(new FileOutputStream(path)); wb.close(); } //your method changes are commented public XSSFWorkbook createLineChart(String name, XSSFWorkbook data) throws Exception { Workbook workbook = new XSSFWorkbook(); //create the external link to datawbname int extwbid = 1; createExternalLinkToWorksheet((XSSFWorkbook)workbook, datawbname, "ChartDataSheet", "rId"+extwbid); Sheet sheet = workbook.createSheet(name); Drawing drawing = sheet.createDrawingPatriarch(); ClientAnchor anchor = drawing.createAnchor(0, 0, 0, 0, 0, 0, 15, 15); Chart lineChart = drawing.createChart(anchor); ChartLegend legend = lineChart.getOrCreateLegend(); legend.setPosition(LegendPosition.BOTTOM); LineChartData chartData = lineChart.getChartDataFactory().createLineChartData(); ChartAxis bottomAxis = lineChart.getChartAxisFactory().createCategoryAxis(AxisPosition.BOTTOM); ValueAxis leftAxis = lineChart.getChartAxisFactory().createValueAxis(AxisPosition.LEFT); leftAxis.setCrosses(AxisCrosses.AUTO_ZERO); Sheet dataSheet = data.getSheetAt(0); ChartDataSource<Number> xData = DataSources.fromNumericCellRange(dataSheet, new CellRangeAddress(0, dataSheet.getLastRowNum(), 0, 0)); ChartDataSource<Number> yData = DataSources.fromNumericCellRange(dataSheet, new CellRangeAddress(0, dataSheet.getLastRowNum(), 1, 1)); LineChartSeries chartSeries = chartData.addSeries(xData, yData); chartSeries.setTitle("A title"); lineChart.plot(chartData, new ChartAxis[] { bottomAxis, leftAxis }); //since dataSheet is an external sheet, the formula in the org.openxmlformats.schemas.drawingml.x2006.chart.CTNumRef //must be prefixed with [1], where 1 is the Id of the linked workbook String catref = ((XSSFChart)lineChart).getCTChart().getPlotArea().getLineChartArray(0).getSerArray(0).getCat().getNumRef().getF(); ((XSSFChart)lineChart).getCTChart().getPlotArea().getLineChartArray(0).getSerArray(0).getCat().getNumRef().setF("[" + extwbid + "]" + catref); String valref = ((XSSFChart)lineChart).getCTChart().getPlotArea().getLineChartArray(0).getSerArray(0).getVal().getNumRef().getF(); ((XSSFChart)lineChart).getCTChart().getPlotArea().getLineChartArray(0).getSerArray(0).getVal().getNumRef().setF("[" + extwbid + "]" + valref); return (XSSFWorkbook)workbook; } //method for creating a external link to a sheet in another workbook public void createExternalLinkToWorksheet(XSSFWorkbook workbook, String wbname, String sheetname, String rIdExtWb) throws Exception { OPCPackage opcpackage = workbook.getPackage(); //creating /xl/externalLinks/externalLink1.xml having link to externalBook with external sheetName PackagePartName partname = PackagingURIHelper.createPartName("/xl/externalLinks/externalLink1.xml"); PackagePart part = opcpackage.createPart(partname, "application/vnd.openxmlformats-officedocument.spreadsheetml.externalLink+xml"); POIXMLDocumentPart externallinkstable = new POIXMLDocumentPart(part) { @Override protected void commit() throws IOException { PackagePart part = getPackagePart(); OutputStream out = part.getOutputStream(); try { ExternalLinkDocument doc = ExternalLinkDocument.Factory.parse( "<externalLink xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\">" +"<externalBook xmlns:r=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships\" r:id=\""+ rIdExtWb + "\">" +"<sheetNames><sheetName val=\"" + sheetname + "\"/></sheetNames>" +"</externalBook>" +"</externalLink>" ); doc.save(out, DEFAULT_XML_OPTIONS); out.close(); } catch (Exception ex) { ex.printStackTrace(); }; } }; //creating the relation to the external workbook in /xl/externalLinks/_rels/externalLink1.xml.rels PackageRelationship packrelship = part.addRelationship(new java.net.URI(wbname), TargetMode.EXTERNAL, "http://schemas.openxmlformats.org/officeDocument/2006/relationships/externalLinkPath", rIdExtWb); //creating the relation to /xl/externalLinks/externalLink1.xml in /xl/_rels/workbook.xml.rels String rIdExtLink = "rId" + (workbook.getRelationParts().size()+1); workbook.addRelation(rIdExtLink, XSSFRelation.EXTERNAL_LINKS, externallinkstable); //creating the <externalReferences><externalReference .../> in /xl/workbook.xml workbook.getCTWorkbook().addNewExternalReferences().addNewExternalReference().setId(rIdExtLink); } public static void main(String[] args) throws Exception { CreateExcelLineChartDataAnotherWorkbook mainObject = new CreateExcelLineChartDataAnotherWorkbook(); } } 

我的新代码提供了一个MyXSSFWorkbook类,它通过为链接工作簿和工作表创buildExternalLinksTable的方法扩展了XSSFWorkbook 。 此代码确实创build了一个ExternalLinksTable ,它使用reflection将此ExternalLinksTable添加到XSSFWorkbook中的ExternalLinksTable列表中。 所以它会进一步使用工作簿。

该方法只需要链接工作簿和链接表的名称。 它pipe理自己的ID。 它返回ExternalLinksTable的ID(因为在/xl/externalLinks/externalLink1.xml是1),所以这个Id可以用作公式中的外部工作簿引用(如[1]ChartDataSheet!$A$1:$A$20 )。

 import java.io.*; import org.apache.poi.ss.usermodel.*; import org.apache.poi.ss.usermodel.charts.*; import org.apache.poi.ss.util.CellRangeAddress; import org.apache.poi.xssf.usermodel.*; import org.apache.poi.xssf.model.ExternalLinksTable; import org.apache.poi.openxml4j.opc.*; import org.apache.poi.POIXMLDocumentPart; import org.openxmlformats.schemas.spreadsheetml.x2006.main.ExternalLinkDocument; import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTExternalReferences; import static org.apache.poi.POIXMLTypeLoader.DEFAULT_XML_OPTIONS; import java.lang.reflect.Field; import java.util.List; import java.util.ArrayList; public class CreateExcelLineChartExternalLinksTable { private static String datawbname = "DataWB.xlsx"; private static String chartwbname = "ChartWB.xlsx"; public CreateExcelLineChartExternalLinksTable() throws Exception { Workbook datawb = createDataSpreadsheet("ChartDataSheet"); saveWorkbook(datawb, "/home/axel/Dokumente/"+datawbname); Workbook chartwb = createLineChart("ChartSheet", (XSSFWorkbook)datawb); saveWorkbook(chartwb, "/home/axel/Dokumente/"+chartwbname); } //your method only partially changed to have sample data public XSSFWorkbook createDataSpreadsheet(String name) { Workbook workbook = new XSSFWorkbook(); Sheet sheet = workbook.createSheet(name); int rowNumber = 0; for(int i = 0; i < 20; i++) { Row row = sheet.createRow(rowNumber++); int columnNumber = 0; row.createCell(columnNumber++).setCellValue(Math.PI*i/10*2); row.createCell(columnNumber++).setCellValue(Math.sin(Math.PI*i/10*2)); } return (XSSFWorkbook)workbook; } //method for saving the workbooks public void saveWorkbook(Workbook wb, String path) throws Exception { wb.write(new FileOutputStream(path)); wb.close(); } //your method changes are commented public XSSFWorkbook createLineChart(String name, XSSFWorkbook data) throws Exception { Workbook workbook = new MyXSSFWorkbook(); Sheet sheet = workbook.createSheet(name); Drawing drawing = sheet.createDrawingPatriarch(); ClientAnchor anchor = drawing.createAnchor(0, 0, 0, 0, 0, 0, 15, 15); Chart lineChart = drawing.createChart(anchor); ChartLegend legend = lineChart.getOrCreateLegend(); legend.setPosition(LegendPosition.BOTTOM); LineChartData chartData = lineChart.getChartDataFactory().createLineChartData(); ChartAxis bottomAxis = lineChart.getChartAxisFactory().createCategoryAxis(AxisPosition.BOTTOM); ValueAxis leftAxis = lineChart.getChartAxisFactory().createValueAxis(AxisPosition.LEFT); leftAxis.setCrosses(AxisCrosses.AUTO_ZERO); Sheet dataSheet = data.getSheetAt(0); ChartDataSource<Number> xData = DataSources.fromNumericCellRange(dataSheet, new CellRangeAddress(0, dataSheet.getLastRowNum(), 0, 0)); ChartDataSource<Number> yData = DataSources.fromNumericCellRange(dataSheet, new CellRangeAddress(0, dataSheet.getLastRowNum(), 1, 1)); LineChartSeries chartSeries = chartData.addSeries(xData, yData); chartSeries.setTitle("A title"); lineChart.plot(chartData, new ChartAxis[] { bottomAxis, leftAxis }); //create the ExternalLinksTable for the linked workbook and sheet int extLinksId = ((MyXSSFWorkbook)workbook).createExternalLinksTableWbSheet(datawbname, "ChartDataSheet"); System.out.println(((XSSFWorkbook)workbook).getExternalLinksTable()); //since dataSheet is an external sheet, the formula in the org.openxmlformats.schemas.drawingml.x2006.chart.CTNumRef //must be prefixed with [1], where 1 is the Id of the linked workbook String catref = ((XSSFChart)lineChart).getCTChart().getPlotArea().getLineChartArray(0).getSerArray(0).getCat().getNumRef().getF(); ((XSSFChart)lineChart).getCTChart().getPlotArea().getLineChartArray(0).getSerArray(0).getCat().getNumRef().setF("["+extLinksId+"]" + catref); String valref = ((XSSFChart)lineChart).getCTChart().getPlotArea().getLineChartArray(0).getSerArray(0).getVal().getNumRef().getF(); ((XSSFChart)lineChart).getCTChart().getPlotArea().getLineChartArray(0).getSerArray(0).getVal().getNumRef().setF("["+extLinksId+"]" + valref); return (XSSFWorkbook)workbook; } public static void main(String[] args) throws Exception { CreateExcelLineChartExternalLinksTable mainObject = new CreateExcelLineChartExternalLinksTable(); } //class which extends XSSFWorkbook and provides a method for creating ExternalLinksTable for linked workbook and sheet private class MyXSSFWorkbook extends XSSFWorkbook { //method for creating ExternalLinksTable for linked workbook and sheet //returns the Id of this ExternalLinksTable int createExternalLinksTableWbSheet(String wbname, String sheetname) throws Exception { List<ExternalLinksTable> elternallinkstablelist = getExternalLinksTable(); int extLinksId = 1; if (elternallinkstablelist != null) extLinksId = elternallinkstablelist.size()+1; OPCPackage opcpackage = getPackage(); //creating /xl/externalLinks/externalLink1.xml having link to externalBook with external sheetName PackagePartName partname = PackagingURIHelper.createPartName("/xl/externalLinks/externalLink"+extLinksId+".xml"); PackagePart part = opcpackage.createPart(partname, "application/vnd.openxmlformats-officedocument.spreadsheetml.externalLink+xml"); OutputStream out = part.getOutputStream(); ExternalLinkDocument doc = ExternalLinkDocument.Factory.parse( "<externalLink xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\">" +"<externalBook xmlns:r=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships\" r:id=\"rId1\">" +"<sheetNames><sheetName val=\"" + sheetname + "\"/></sheetNames>" +"</externalBook>" +"</externalLink>" ); doc.save(out, DEFAULT_XML_OPTIONS); out.close(); //creating the relation to the external workbook in /xl/externalLinks/_rels/externalLink1.xml.rels PackageRelationship packrelship = part.addRelationship(new java.net.URI(wbname), TargetMode.EXTERNAL, "http://schemas.openxmlformats.org/officeDocument/2006/relationships/externalLinkPath", "rId1"); ExternalLinksTable externallinkstable = new ExternalLinksTable(part); //creating the relation to /xl/externalLinks/externalLink1.xml in /xl/_rels/workbook.xml.rels String rIdExtLink = "rId" + (getRelationParts().size()+1); addRelation(rIdExtLink, XSSFRelation.EXTERNAL_LINKS, externallinkstable); //creating the <externalReferences><externalReference .../> in /xl/workbook.xml CTExternalReferences externalreferences = getCTWorkbook().getExternalReferences(); if (externalreferences == null) externalreferences = getCTWorkbook().addNewExternalReferences(); externalreferences.addNewExternalReference().setId(rIdExtLink); Field externalLinksField = XSSFWorkbook.class.getDeclaredField("externalLinks"); externalLinksField.setAccessible(true); @SuppressWarnings("unchecked") //we know the problem and expect runtime error if it possibly occurs List<ExternalLinksTable> externalLinks = (ArrayList<ExternalLinksTable>)externalLinksField.get(this); if (externalLinks == null) { externalLinks = new ArrayList<ExternalLinksTable>(); externalLinks.add(externallinkstable); externalLinksField.set(this, externalLinks); } else { externalLinks.add(externallinkstable); } return extLinksId; } } }