将所有类型的RELATIONS从一个工作簿复制到新工作簿Apache POI XSSFWorkbook

时间:2016-12-24 17:09:03

标签: java excel apache-poi

我一直在尝试从一个工作簿中获取关系并将它们复制到另一个新创建的工作簿中。 到目前为止,我已经尝试过这个:

XSSFWorkbook oldWB = new XSSFWorkbook(new File("F:\\pivottablesurvey.xlsx")); //src workbook
XSSFWorkbook newWB = new XSSFWorkbook(); //target workbook

POIXMLDocument upcastOldwb = oldWB; //Upcasting
POIXMLDocument upcastNewwb = newWB; //Upcasting

for (PackageRelationship pr : upcastOldwb.getPackagePart().getRelationships()) {
   upcastNewwb.getPackagePart().getRelatedPart(pr).addRelationship(pr.getTargetURI(),pr.getTargetMode(), pr.getRelationshipType());       
}

此时,我收到此错误:

Exception in thread "main" java.lang.IllegalArgumentException: Relationship id=rId1 - container=org.apache.poi.openxml4j.opc.ZipPackage@5ffdc730 - relationshipType=http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet - source=/xl/workbook.xml - target=/xl/worksheets/sheet1.xml,targetMode=INTERNAL doesn't start with this part /xl/workbook.xml

首先,我承认我甚至不知道我采取的方法是否正确。我只是想将关系从一个工作簿复制到另一个工作簿。 任何帮助将不胜感激。

由于

修改2

当你说那部分必须首先存在时,这是你所指的吗?

XSSFWorkbook oldWB = new XSSFWorkbook(new File("F:\\pivottablesurvey.xlsx")); //src workbook
XSSFWorkbook newWB = new XSSFWorkbook(); //target workbook

POIXMLDocument upcastOldwb = oldWB; //Upcasting
POIXMLDocument upcastNewwb = newWB; //Upcasting
//Different code from above (the actual question). Is this what you thought I missed?
for (PackageRelationship pr : upcastOldwb.getPackagePart().getRelationships()) {
         URI target = pr.getTargetURI();
           if(target.getFragment() != null) {
              String t = target.toString();
              try {
                 target = new URI( t.substring(0, t.indexOf('#')) );
              } catch(URISyntaxException e) {
                 throw new InvalidFormatException("Invalid target URI: " + target);
              }
           }               
           PackagePartName relName = PackagingURIHelper.createPartName(target);
           upcastNewwb.getPackagePart().getPackage().createPart(relName, upcastOldwb.getPackagePart().getContentType());               
    }

修改1:

我的最终目标是将工作表从一个工作簿复制到另一个工作簿。还有其他建议/解决方案。我甚至自己实现了一个,而没有看其他解决方案。

我实施了建议given here by Gagravarr.事实证明,我的实施方式与SO和coderanch上的其他解决方案相同99%。但是这个解决方案存在问题。如果工作表包含表格,图片,图表等,那么这些解决方案效果不佳。

然后我想到了一种将工作表复制到新工作簿的聪明方法:By process of Elimination!这个解决方案是最好的解决方案。它保持一切完整,没有图形,图表,图片会打破。但正如你所知,这是一种愚蠢的方式。不是很酷的方式。所以我想实现一些正确的方法。或者是专业的开发者方式。

为此,我研究了XSSFWorkbook.cloneSheet(...)方法以及Apache的开发人员如何实现它。我想复制它。到目前为止,在我的尝试中,每件事都按照计划进行,只有一个小问题。而这个问题是上面的原始问题。我先告诉你我的代码:

public static void main(String[] args) throws Exception {
XSSFWorkbook oldWB = new XSSFWorkbook(new File("F:\\faraz\\Documents\\pivottablesurvey.xlsx"));
XSSFWorkbook newWB = new XSSFWorkbook();

for (int i = 0; i < oldWB.getNumberOfSheets(); i++) {
    XSSFSheet sheetFromOldWB = (XSSFSheet) oldWB.getSheetAt(i);
    XSSFSheet sheetForNewWB = (XSSFSheet) newWB.createSheet(sheetFromOldWB.getSheetName());
    /*
     * Behold! Below this point, I am trying to mimic XSSFWorkbook.cloneSheet(...) method
     */
    List<RelationPart> rels = sheetFromOldWB.getRelationParts();            
     XSSFDrawing dg = null;
        for(RelationPart rp : rels) {
            POIXMLDocumentPart r = rp.getDocumentPart();
            if(r instanceof XSSFDrawing) {                  
                dg = (XSSFDrawing)r;
                continue;
            }                   
            addRelation(rp, sheetForNewWB); //This is a private method in XSSFWorkbook class so I copied this method over to this class
        }               
        try {
            for(PackageRelationship pr : sheetFromOldWB.getPackagePart().getRelationships()) {
                if (pr.getTargetMode() == TargetMode.EXTERNAL) {
                    sheetForNewWB.getPackagePart().addExternalRelationship
                        (pr.getTargetURI().toASCIIString(), pr.getRelationshipType(), pr.getId());
                }
            }
        } catch (InvalidFormatException e) {
            throw new POIXMLException("Failed to clone sheet", e);
        }                   
        OutputStream out = new ByteArrayOutputStream();
        Method writeReflect = sheetFromOldWB.getClass().
                getDeclaredMethod("write", OutputStream.class); //I had to use reflection here to get it to work because write(OutputStream os) is a private method in XSSFWorkbook class               
        writeReflect.setAccessible(true);
        Object w = writeReflect.invoke(sheetFromOldWB,out);             
        Method readReflect = sheetFromOldWB.getClass().
                getDeclaredMethod("read", InputStream.class);   //Same reason as above              
        readReflect.setAccessible(true);
        Object r = readReflect.invoke(sheetForNewWB,new ByteArrayInputStream(((ByteArrayOutputStream) out).toByteArray()));             
        CTWorksheet ct = sheetForNewWB.getCTWorksheet();
            if(ct.isSetLegacyDrawing()) {
                System.out.println("Cloning sheets with comments is not yet supported.");
                ct.unsetLegacyDrawing();
            }
            if (ct.isSetPageSetup()) {
                System.out.println("Cloning sheets with page setup is not yet supported.");
                ct.unsetPageSetup();
            }
            sheetForNewWB.setSelected(false); 
            if (dg != null) {
                if(ct.isSetDrawing()) {                       
                    ct.unsetDrawing();
                }
                XSSFDrawing clonedDg = sheetForNewWB.createDrawingPatriarch();                     
                clonedDg.getCTDrawing().set(dg.getCTDrawing());
                clonedDg = sheetForNewWB.createDrawingPatriarch();                 
                List<RelationPart> srcRels = sheetFromOldWB.createDrawingPatriarch().getRelationParts();
                for (RelationPart rp : srcRels) {
                    addRelation(rp, clonedDg);
                }
            }
}
FileOutputStream fileOut = new FileOutputStream("F:\\faraz\\Documents\\output.xlsx");
newWB.write(fileOut);
oldWB.close();
newWB.close();
fileOut.close();
}

private static void addRelation(RelationPart rp, POIXMLDocumentPart target) {
PackageRelationship rel = rp.getRelationship();
if (rel.getTargetMode() == TargetMode.EXTERNAL) {
    target.getPackagePart().addRelationship(
        rel.getTargetURI(), rel.getTargetMode(), rel.getRelationshipType(), rel.getId());
} else {        
    XSSFRelation xssfRel = XSSFRelation.getInstance(rel.getRelationshipType());

    if (xssfRel == null) {
        throw new POIXMLException("Can't clone sheet - unknown relation type found: "+rel.getRelationshipType());
    } 
    **target.addRelation(rel.getId(), xssfRel, rp.getDocumentPart());**
}
}

带有双星号target.addRelation(rel.getId(), xssfRel, rp.getDocumentPart());的那条线给我带来了麻烦。当我按原样运行该程序时,我收到此错误:

Exception in thread "main" java.lang.IllegalArgumentException: No part found for relationship id=rId1 - container=org.apache.poi.openxml4j.opc.ZipPackage@50c91c07 - relationshipType=http://schemas.openxmlformats.org/officeDocument/2006/relationships/pivotTable - source=/xl/worksheets/sheet1.xml - target=/xl/pivotTables/pivotTable1.xml,targetMode=INTERNAL
at org.apache.poi.openxml4j.opc.PackagePart.getRelatedPart(PackagePart.java:487)
at org.apache.poi.POIXMLDocumentPart.findExistingRelation(POIXMLDocumentPart.java:378)
at org.apache.poi.POIXMLDocumentPart.addRelation(POIXMLDocumentPart.java:343)
at oldmain.addRelation(oldmain.java:112)
at oldmain.main(oldmain.java:50)

此时正在寻找关系!实际上,我应该说我相信它在工作簿中寻找关系,因为我不确定。但它无法在那里找到它们因为我没有在同一工作簿中克隆工作表。

这是我在这里的实际问题。它在这里寻找什么?我认为是正确的吗?它实际上是在寻找工作簿中的一些关系吗?如果我的想法是正确的,那么我需要将所有关系从源工作簿复制到新工作簿。

有一件事,如果我只是注释掉那一行,那么方法就可以了。它会复制所有内容,但图形或图片看起来不太好。我的意思是,它会复制整数和字符串等,但图形,图片和图表将会丢失。让我告诉你我的意思:

资料表: Source Sheet

结果表: Result Sheet

资料表: Source Sheet

结果表: Result Sheet

你看,它有点工作但不完全。我相信这是因为它缺少某种关系。现在,为什么我认识到这一点是因为我挖掘了那条线,看看它在调用什么以及它想要什么。

那么,是否可以将所有关系从一个工作簿复制到另一个工作簿?这实际上是我需要使用此代码吗?

再次感谢,我将非常感谢任何帮助。

0 个答案:

没有答案