Java 将所有类型的关系从一个工作簿复制到新工作簿Apache POI XSSF工作簿
我一直试图从一个工作簿中提取关系,并将它们复制到另一个新创建的工作簿中。 到目前为止,我已经尝试过:Java 将所有类型的关系从一个工作簿复制到新工作簿Apache POI XSSF工作簿,java,excel,apache-poi,Java,Excel,Apache Poi,我一直试图从一个工作簿中提取关系,并将它们复制到另一个新创建的工作簿中。 到目前为止,我已经尝试过: XSSFWorkbook oldWB = new XSSFWorkbook(new File("F:\\pivottablesurvey.xlsx")); //src workbook XSSFWorkbook newWB = new XSSFWorkbook(); //target workbook POIXMLDocument upcastOldwb = oldWB; //Upcastin
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
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)
首先,我承认我甚至不知道我所采取的方法是否正确。我只是尝试将关系从一个工作簿复制到另一个工作簿。
任何帮助都将不胜感激
谢谢
编辑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:
我的最终目标是将工作表从一个工作簿复制到另一个工作簿。还有其他建议/解决方案。我甚至自己实现了一个,而没有考虑其他解决方案
事实证明,我实现了这个建议,我的实现与在SO和coderanch上找到的其他解决方案99%相同。但这一解决方案存在一个问题。如果工作表包含表格、图片、图表等,那么这些解决方案就不能很好地工作
然后我想到了一个聪明的方法将工作表复制到新工作簿中:这个解决方案是其他解决方案中最好的。它保持一切完好无损,没有任何图形、图表、图片会断裂。但正如你所知道的,这是一种令人讨厌的方式。不是很酷的方式。所以我想实现一些正确的方法。或者以专业开发人员的方式
为此,我研究了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());**
}
}
在这一点上,它正在寻找关系!实际上,我应该说我相信它是在工作簿中寻找关系的,因为我不确定。但它无法在那里找到它们,因为我没有在同一工作簿中克隆工作表
这是我真正的问题。它在这里找什么?我认为正确吗?它实际上是在工作簿中寻找一些关系吗?如果我的想法是正确的,那么我需要将源工作簿中的所有关系复制到新工作簿中
有一件事,如果我只是注释掉那一行,那么这个方法就可以了。它会复制所有的东西,但是图形或图片看起来不好。我的意思是,它会复制整数和字符串等,但图形、图片和图表会丢失。让我告诉你我的意思:
来源表:
结果表:
来源表:
结果表:
你看,它在某种程度上起作用了,但还没有完全发挥作用。我相信这是因为它缺少了某种关系。现在,我之所以意识到这一点,是因为我在那条线里面挖掘,看看它在呼唤什么,它想要什么
那么,是否可以将所有关系从一个工作簿复制到另一个工作簿?这真的是我工作所需要的吗
再次感谢您,我将非常感谢您的帮助 你想复制什么样的关系,为什么?大多数都是内置的excel链接,例如工作簿到工作表,因此您不希望复制,因为POI会在您添加/删除工作表时处理这些问题。对于我来说,复制整个工作簿,然后删除所有不需要的工作表是一种正确的方法。这个应该是什么“黑客”?尝试创建一个新工作簿,然后将工作表从另一个工作簿克隆到另一个工作簿中,在我看来,这似乎是一种“黑客”方式,因为您已经看到工作表与工作簿的连接有多么紧密。在您可以为某个零件添加关系之前,该零件必须存在。我看不出复制其他部分的任何逻辑,你错过了吗?如果你想要轻松的生活,只需复制Excel文件并使用Apache POI删除你不想要的工作表。如果你真的想在这个非常低的层次上乱搞,你必须花一些时间阅读关于XLSX格式和OPC格式的Microsoft文件格式文档,你的问题可能不仅仅是POI API,更多的是理解文件格式是如何工作的……你想复制什么样的关系,为什么?大多数都是内置的excel链接,例如工作簿到工作表,因此您不希望复制,因为POI会在您添加/删除工作表时处理这些问题。对于我来说,复制整个工作簿,然后删除所有不需要的工作表是一种正确的方法。这个应该是什么“黑客”?尝试创建一个新工作簿,然后将工作表从另一个工作簿克隆到另一个工作簿中,在我看来,这似乎是一种“黑客”方式,因为您已经看到工作表与工作簿的连接有多么紧密。在您可以为某个零件添加关系之前,该零件必须存在。我看不出复制其他部分的任何逻辑,你错过了吗?如果你想要轻松的生活,只需复制Excel文件并使用Apache POI删除你不想要的工作表。如果你真的想在这个非常低的层次上乱搞,你必须花一些时间阅读关于XLSX格式和OPC格式的Microsoft文件格式文档,你的问题可能不仅仅是POI API,更多的是理解文件格式是如何工作的。。。