2017-10-01 20 views
10

Tôi có một sổ làm việc có một số dữ liệu trong đó. Tôi lấy sổ làm việc đó và tạo một sổ làm việc khác với biểu đồ dạng đường trong đó dựa trên dữ liệu trong sổ làm việc khác. Mã chạy tốt, nhưng bất cứ khi nào tôi mở tệp biểu đồ, tôi nhận được cảnh báo We can't update some of the links in your workbook right now. Nếu tôi nhấp vào nút Edit Links... trong menu cảnh báo, điều đó cho thấy không thể tìm thấy bảng tính dữ liệu. Nếu tôi nhấp vào Change Source... và chọn sổ làm việc phù hợp thì nó hoạt động tốt. Tại sao điều này? POI có thể không giữ liên kết giữa hai tệp không?Tại sao tôi không thể liên kết một sổ làm việc với một sổ làm việc khác trong Apache POI?

Mã của tôi:

Để tạo bảng tính dữ liệu:

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; 
} 

Để tạo bảng tính đồ thị:

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; 
} 

Trả lời

5

Tạo liên kết ngoài trong XSSF không được thực hiện tốt đến bây giờ. Có ExternalLinksTable nhưng nếu bạn nhìn vào Uses of this Class thì bạn sẽ thấy rằng chỉ có cung cấp đọc những liên kết bên ngoài mà không phải tạo và viết.

Vì vậy, chúng tôi cần làm việc với các đối tượng cấp thấp. Và chúng tôi cần kiến ​​thức về các phụ thuộc nội bộ của các liên kết bên ngoài này trong Office OpenXML *.xlsx lưu trữ ZIP.

Các công việc sau đây làm cả sổ làm việc được lưu trữ trong cùng một thư mục.

Mã này chủ yếu là mã được cung cấp của bạn được thêm bằng phương pháp tạo liên kết ngoài tới trang tính trong một sổ làm việc khác. Phương pháp này đang sử dụng các đối tượng mức thấp và không phải là rất chung có thể sử dụng được, nhưng nó sẽ hiển thị nguyên tắc.

Các thay đổi khác đối với mã của bạn cũng được nhận xét.

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(); 
} 

} 

mã mới của tôi cung cấp một lớp MyXSSFWorkbook kéo dài XSSFWorkbook bởi một phương pháp để tạo ExternalLinksTable cho bảng tính liên kết và tấm. Mã này thực sự tạo ra một ExternalLinksTable và nó sử dụng phản ánh để thêm này ExternalLinksTable vào danh sách ExternalLinksTable s trong XSSFWorkbook. Vì vậy, nó sẽ có thể nhận được trong hơn nữa bằng cách sử dụng bảng tính.

Phương pháp chỉ cần tên của sổ làm việc được liên kết và trang tính được liên kết. Nó tự quản lý các Id. Nó trả về Id của ExternalLinksTable (như là 1 trong /xl/externalLinks/externalLink1.xml. Vì vậy, Id này có thể được sử dụng như tài liệu tham khảo bảng tính bên ngoài trong công thức (như là 1 trong [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; 
    } 
} 
} 
+0

Tốt câu trả lời. Nó hoạt động, nhưng tôi đang chờ đợi để xem liệu có ai đăng tải giải pháp đơn giản và linh hoạt hơn hay không. Như bạn đã nói trong câu trả lời, mức độ này rất thấp và không thể sử dụng chung được –

+0

@Alex Quilliam: Tôi đã cung cấp mã mới có thể sử dụng chung hơn. –

Các vấn đề liên quan