簡體   English   中英

如果工作表包含表格,XSSFWorkbook cloneSheet 會損壞工作簿

[英]XSSFWorkbook cloneSheet corrupts workbook if sheet contains a table

我想克隆 Excel 工作表及其所有內容。 我嘗試了XSSFWorkbook cloneSheet 方法,但如果我的工作表包含 Excel 表,則工作簿似乎已損壞。 請參閱下面的示例工作簿,其中包含一個簡單的表格:

在此處輸入圖像描述

當我嘗試打開 output 工作簿時,我收到一條提示,告訴我文件已損壞,需要修復。 如果我恢復工作簿,很明顯該表沒有被正確復制; 原來的總計行現在是一個數據行。

在此處輸入圖像描述

try (InputStream is = Table.class.getResourceAsStream("table.xlsx")) {
    XSSFWorkbook workbook = new XSSFWorkbook(is);

    workbook.cloneSheet(0, "Test");

    try (OutputStream fileOut = new FileOutputStream("table-2.xlsx")) {
        workbook.write(fileOut);
    }

} catch (IOException e) {
    e.printStackTrace();
}

我將如何 go 關於復制此表? 任何幫助表示贊賞!

XSSFWorkbook.cloneSheet克隆工作表。 但它沒有考慮其中可能定義的表。 它只是克隆表引用。 但是工作表中的兩個表范圍不能引用同一個表引用。 表本身需要克隆。 這就是損壞工作簿的原因。

我試圖通過編寫一個方法cloneTables(XSSFSheet sheet)來解決這個問題,該方法只是在工作表中創建每個表的克隆,然后每個表引用它們自己的表引用。 我考慮表 styles、自動過濾器、總計行和計算列公式。 我希望我沒有忽略什么,但我對此表示懷疑。

該代碼使用當前的apache poi 5.2.2進行了測試和工作。

它還包含以下錯誤的修復:

XSSFTable.updateHeaders在使用當前 Excel 版本創建的 Excel 工作簿中失敗。 這是因為測試row.getCTRow().validate()由於使用了新的名稱空間,它總是為假。 請參閱Renaming headers of XSSFTable with Apache Poi 導致損壞的 XLSX-file

XSSFSheet.removeTable不會從工作表中刪除指向表格部件參考的鏈接。

完整的示例進行測試:

import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.*;
import org.apache.poi.ss.util.*;
import org.apache.poi.ss.SpreadsheetVersion;

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

import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTTableColumn;

class ExcelCloneSheetHavingTable {
        
 static void updateHeaders(XSSFTable table) {
  XSSFSheet sheet = (XSSFSheet)table.getParent();
  CellReference ref = table.getStartCellReference();

  if (ref == null) return;

  int headerRow = ref.getRow();
  int firstHeaderColumn = ref.getCol();
  XSSFRow row = sheet.getRow(headerRow);
  DataFormatter formatter = new DataFormatter();
  
  if (row != null /*&& row.getCTRow().validate()*/) { // see bug: https://stackoverflow.com/questions/55532006/renaming-headers-of-xssftable-with-apache-poi-leads-to-corrupt-xlsx-file/55539181#55539181
   int cellnum = firstHeaderColumn;
   org.openxmlformats.schemas.spreadsheetml.x2006.main.CTTableColumns ctTableColumns = table.getCTTable().getTableColumns();
   if(ctTableColumns != null) {
    for (org.openxmlformats.schemas.spreadsheetml.x2006.main.CTTableColumn col : ctTableColumns.getTableColumnList()) {
     XSSFCell cell = row.getCell(cellnum);
     if (cell != null) {
      String colName = formatter.formatCellValue(cell);
      colName = colName.replace("\n", "_x000a_");
      colName = colName.replace("\r", "_x000d_");
      col.setName(colName);
     }
     cellnum++;
    }
   }
  }
  //tableColumns = null;
  //columnMap = null;
  //xmlColumnPrs = null;
  //commonXPath = null;
  try {
   java.lang.reflect.Field tableColumns = XSSFTable.class.getDeclaredField("tableColumns");
   tableColumns.setAccessible(true);
   tableColumns.set(table, null);
   java.lang.reflect.Field columnMap = XSSFTable.class.getDeclaredField("columnMap");
   columnMap.setAccessible(true);
   columnMap.set(table, null);
   java.lang.reflect.Field xmlColumnPrs = XSSFTable.class.getDeclaredField("xmlColumnPrs");
   xmlColumnPrs.setAccessible(true);
   xmlColumnPrs.set(table, null);
   java.lang.reflect.Field commonXPath = XSSFTable.class.getDeclaredField("commonXPath");
   commonXPath.setAccessible(true);
   commonXPath.set(table, null);
  } catch (Exception ex) {
   ex.printStackTrace();   
  }
 }
 
 static String getSubtotalFormulaStartFromTotalsRowFunction(int intTotalsRowFunction) {
  final int INT_NONE = 1;
  final int INT_SUM = 2;
  final int INT_MIN = 3;
  final int INT_MAX = 4;
  final int INT_AVERAGE = 5;
  final int INT_COUNT = 6;
  final int INT_COUNT_NUMS = 7;
  final int INT_STD_DEV = 8;
  final int INT_VAR = 9;
  final int INT_CUSTOM = 10;
  String subtotalFormulaStart = null;
  switch (intTotalsRowFunction) {
   case INT_NONE:
    subtotalFormulaStart = null;
    break;
   case INT_SUM:
    subtotalFormulaStart = "SUBTOTAL(109";
    break;
   case INT_MIN:
    subtotalFormulaStart = "SUBTOTAL(105";
    break;
   case INT_MAX:
    subtotalFormulaStart = "SUBTOTAL(104";
    break;
   case INT_AVERAGE:
    subtotalFormulaStart = "SUBTOTAL(101";
    break;
   case INT_COUNT:
    subtotalFormulaStart = "SUBTOTAL(103";
    break;
   case INT_COUNT_NUMS:
    subtotalFormulaStart = "SUBTOTAL(102";
    break;
   case INT_STD_DEV:
    subtotalFormulaStart = "SUBTOTAL(107";
    break;
   case INT_VAR:
    subtotalFormulaStart = "SUBTOTAL(110";
    break;
   case INT_CUSTOM:
    subtotalFormulaStart = null;
    break;
   default:
    subtotalFormulaStart = null;   
  }
  return subtotalFormulaStart;  
 }
    
 static void cloneTables(XSSFSheet sheet) {
  for (XSSFTable table : sheet.getTables()) {
   // clone table
   XSSFTable clonedTable = sheet.createTable(table.getArea());
   //clonedTable.updateHeaders(); // don't work, see bug: https://stackoverflow.com/questions/55532006/renaming-headers-of-xssftable-with-apache-poi-leads-to-corrupt-xlsx-file/55539181#55539181
   updateHeaders(clonedTable);
   
   // clone style
   clonedTable.setStyleName(table.getStyleName());
   XSSFTableStyleInfo style = (XSSFTableStyleInfo)table.getStyle();
   XSSFTableStyleInfo clonedStyle = (XSSFTableStyleInfo)clonedTable.getStyle();
   if (style != null && clonedStyle != null) {
    clonedStyle.setShowColumnStripes(style.isShowColumnStripes());
    clonedStyle.setShowRowStripes(style.isShowRowStripes());
    clonedStyle.setFirstColumn(style.isShowFirstColumn());
    clonedStyle.setLastColumn(style.isShowLastColumn());
   }
   
   //clone autofilter
   clonedTable.getCTTable().setAutoFilter(table.getCTTable().getAutoFilter());
   
   //clone totalsrow
   int totalsRowCount = table.getTotalsRowCount();
   if (totalsRowCount == 1) { // never seen more than one totals row
    XSSFRow totalsRow = sheet.getRow(clonedTable.getEndCellReference().getRow());
    if (clonedTable.getCTTable().getTableColumns().getTableColumnList().size() > 0) {
     clonedTable.getCTTable().setTotalsRowCount(totalsRowCount);
     for (int i = 0; i < clonedTable.getCTTable().getTableColumns().getTableColumnList().size(); i++) {
      org.openxmlformats.schemas.spreadsheetml.x2006.main.CTTableColumn tableCol = table.getCTTable().getTableColumns().getTableColumnList().get(i);
      org.openxmlformats.schemas.spreadsheetml.x2006.main.CTTableColumn clonedTableCol = clonedTable.getCTTable().getTableColumns().getTableColumnList().get(i);
      clonedTableCol.setTotalsRowFunction(tableCol.getTotalsRowFunction());
      int intTotalsRowFunction = clonedTableCol.getTotalsRowFunction().intValue();
      sheet.getWorkbook().setCellFormulaValidation(false);
      if (intTotalsRowFunction == 10) { //custom
       org.openxmlformats.schemas.spreadsheetml.x2006.main.CTTableFormula totalsRowFormula = tableCol.getTotalsRowFormula();
       clonedTableCol.setTotalsRowFormula(totalsRowFormula);
       totalsRow.getCell(clonedTable.getStartCellReference().getCol()+i).setCellFormula(totalsRowFormula.getStringValue());
      } else if (intTotalsRowFunction == 1) { //none
       //totalsRow.getCell(clonedTable.getStartCellReference().getCol()+i).setBlank();
      } else {
       String subtotalFormulaStart = getSubtotalFormulaStartFromTotalsRowFunction(intTotalsRowFunction);
       if (subtotalFormulaStart != null) 
        totalsRow.getCell(clonedTable.getStartCellReference().getCol()+i).setCellFormula(subtotalFormulaStart + "," + clonedTable.getName() +"[" + clonedTableCol.getName()+ "])");
      }
     }
    }
   }
   
   // clone calculated column formulas
   if (clonedTable.getCTTable().getTableColumns().getTableColumnList().size() > 0) {
    clonedTable.getCTTable().setTotalsRowCount(totalsRowCount);
    for (int i = 0; i < clonedTable.getCTTable().getTableColumns().getTableColumnList().size(); i++) {
     org.openxmlformats.schemas.spreadsheetml.x2006.main.CTTableColumn tableCol = table.getCTTable().getTableColumns().getTableColumnList().get(i);
     org.openxmlformats.schemas.spreadsheetml.x2006.main.CTTableColumn clonedTableCol = clonedTable.getCTTable().getTableColumns().getTableColumnList().get(i);
     if (tableCol.getCalculatedColumnFormula() != null) {
      clonedTableCol.setCalculatedColumnFormula(tableCol.getCalculatedColumnFormula());
      org.openxmlformats.schemas.spreadsheetml.x2006.main.CTTableFormula calculatedColumnFormula = clonedTableCol.getCalculatedColumnFormula();
      String formula = tableCol.getCalculatedColumnFormula().getStringValue();
      String clonedFormula = formula.replace(table.getName(), clonedTable.getName());
      calculatedColumnFormula.setStringValue(clonedFormula);
      int rFirst = clonedTable.getStartCellReference().getRow() + clonedTable.getHeaderRowCount();
      int rLast = clonedTable.getEndCellReference().getRow() - clonedTable.getTotalsRowCount();
      int c = clonedTable.getStartCellReference().getCol() + i;
      sheet.getWorkbook().setCellFormulaValidation(false);
      for (int r = rFirst; r <= rLast; r++) {
       sheet.getRow(r).getCell(c).setCellFormula(clonedFormula);
      }       
     }
    }
   }
   
   // remove old table   
   String rId = sheet.getRelationId(table);
   sheet.removeTable(table);
   // remove links to the table part reference
   org.openxmlformats.schemas.spreadsheetml.x2006.main.CTTableParts tblParts = sheet.getCTWorksheet().getTableParts();
   if (tblParts != null &&  tblParts.getTablePartList().size() > 0) {
    for (int i = 0; i < tblParts.getTablePartList().size(); i++) {
     org.openxmlformats.schemas.spreadsheetml.x2006.main.CTTablePart tblPart = tblParts.getTablePartArray​(i);    
     if(tblPart.getId().equals(rId)) {
      tblParts.removeTablePart​(i);
     }   
    }     
   }
   
  }
 }

 public static void main(String[] args) throws Exception {
  try (Workbook workbook = WorkbookFactory.create(new FileInputStream("SAMPLE.xlsx"));
       FileOutputStream out = new FileOutputStream("SAMPLE_NEW.xlsx")) {

   XSSFSheet sheet = ((XSSFWorkbook)workbook).cloneSheet(0, "Test");
   cloneTables(sheet);
   
   workbook.write(out);
  } 
 }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM