首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >POI 3.17在克隆的工作表中创建单元格注释会创建不一致的xlsx

POI 3.17在克隆的工作表中创建单元格注释会创建不一致的xlsx
EN

Stack Overflow用户
提问于 2018-07-27 13:33:51
回答 1查看 893关注 0票数 1

我使用cloneSheet方法在已经包含注释的同一个工作簿中复制工作表。之后,新的注释添加到这个新的工作表中,并保存excel。

当使用Excel 365打开文件时,它抱怨/xl/Comms1.xml并恢复该文件。新创建的注释是可用的。在恢复过程中,将删除来自克隆的注释。

打开zip文件并查看/xl/Comms1.xml,它显示了一个不同之处。

这是cloneSheet方法的问题还是微软正在使用新的方法?

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2018-07-28 08:38:38

随着apache poi项目的成熟,它还远未完成。因此,需要使用它的人必须知道所使用的文件系统的内部结构。

那么,如何在Excel的Office (*.xlsx)文件系统中存储注释呢?

整个文件系统是一个ZIP存档。第一页的工作表数据在该ZIP内的/xl/worksheets/sheet1.xml中。那里的XML

代码语言:javascript
复制
...
<legacyDrawing r:id="rId2"/>
...

它指向在第一个工作表的关系部分中具有rId2的遗留rId2

第一个工作表的关系部分XML是/xl/worksheets/_rels/sheet1.xml.rels,如下所示

代码语言:javascript
复制
<Relationships>
 <Relationship Id="rId3" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/comments" Target="../comments1.xml"/>
 <Relationship Id="rId2" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/vmlDrawing" Target="../drawings/vmlDrawing1.vml"/>
...
</Relationships>

因此,rId2指向/xl/drawings/vmlDrawing1.vmlrId3指向/xl/comments1.xml

因此,vmlDrawing1.vml包含工作表上注释形状的锚点,而‘Comms1.xml’包含注释‘内容。

现在,方法public XSSFSheet cloneSheet(int sheetNum,String newName) of XSSFWorkbook在做什么?

一开始,它复制了所有纸张的关系。因此,还复制了与VMLDrawingComments部件的关系。因此,如果我们是克隆第一页,那么在克隆之后,/xl/worksheets/_rels/sheet2.xml.rels将具有与/xl/worksheets/_rels/sheet1.xml.rels相同的内容。

但是它说:“带有注释的克隆页还不被支持。”并从工作表的XML中移除<legacyDrawing r:id="rId2"/>。但是以前复制的关系并没有被删除。

因此,我们有一个克隆的工作表,没有注释链接在工作表中,但与注释和它们的形状集有关系。

如果我们现在在克隆的工作表中创建新的注释,那么在/xl/worksheets/_rels/sheet2.xml.rels中也会创建包含它的关系的新的/xl/worksheets/_rels/sheet2.xml.rels。在那之后,我们有一个/xl/worksheets/_rels/sheet2.xml.rels,它指向/xl/drawings/vmlDrawing1.vml,也指向/xl/drawings/vmlDrawing2.vml。但这是不允许的,因此Excel在打开时抛出错误,并建议进行修复。

此外,新创建的注释存储在/xl/comments1.xml中,这也是错误的,因为每个工作表都需要自己的注释部分。这是因为在克隆XSSFSheet时,字段private CommentsTable sheetComments也会被克隆,并且现在包含源表的旧注释表。

因此,为了能够在克隆的表中创建注释,我们需要消除错误的关系,也需要消除CommentsTable字段中的错误XSSFSheet

示例:

代码语言:javascript
复制
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.*;

import org.apache.poi.POIXMLDocumentPart;
import org.apache.poi.POIXMLDocumentPart.RelationPart;

import java.io.FileInputStream;
import java.io.FileOutputStream;

import java.lang.reflect.Field;

class ExcelCloneSheetHavingComments {

 public static void main(String[] args) throws Exception {

  Workbook workbook = WorkbookFactory.create(new FileInputStream("ExcelHavingComments.xlsx"));

  Sheet sheetClone = workbook.cloneSheet(0);
  workbook.setSheetName(workbook.getSheetIndex(sheetClone), "Cloned first Sheet");

  if (sheetClone instanceof XSSFSheet) {
   XSSFSheet xssfSheet = (XSSFSheet)sheetClone;

   // get rid of the wrong relations
   for (POIXMLDocumentPart.RelationPart relationPart : xssfSheet.getRelationParts()) {
    if (relationPart.getDocumentPart() instanceof org.apache.poi.xssf.usermodel.XSSFVMLDrawing
     || relationPart.getDocumentPart() instanceof org.apache.poi.xssf.model.CommentsTable) {
     relationPart.getRelationship().getSource().removeRelationship(relationPart.getRelationship().getId());
    } 
   }

   // get rid of the wrong org.apache.poi.xssf.model.CommentsTable
   Field sheetComments = XSSFSheet.class.getDeclaredField("sheetComments"); 
   sheetComments.setAccessible(true); 
   sheetComments.set(xssfSheet, null);
  }  


  Drawing drawing = sheetClone.createDrawingPatriarch();
  Comment comment = drawing.createCellComment(drawing.createAnchor(0, 0, 0, 0, 2, 1, 4, 4));
  comment.setString(new XSSFRichTextString("Comment in Cell C2 in cloned sheet"));


  workbook.write(new FileOutputStream("CopyOfExcelHavingComments.xlsx"));
  workbook.close();

 }
}

这不会复制源表中的注释。当然,现在也可以在源表中使用Sheet.getCellComments,然后在克隆表中使用Drawing.createCellComment

票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/51559133

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档