使用
apache poi
的主要问题是它需要对
Microsoft Office
及其文件存储有详细了解才能使用。许多功能只是半成品,新版本中经常出现退步(已解决的错误再次出现)。
所以您的需求:“使用Apache POI扩展Excel中的现有表格”只是简单地使用
apache poi
是不可能实现的。我们必须知道
Office Open XML
文件
*.xlsx
只是可以解压缩的
ZIP
归档文件。解压缩后,我们可以找到用于存储表格的
/xl/tables/table1.xml
。我们可以分析这个
XML
并将其与使用
Excel
的
GUI
创建的
XML
进行比较。因此,我们可以发现由于
apache poi
的缺陷而导致的问题。在
/xl/tables/sheet1.xml
中也是如此。
此外,我们需要知道
apache poi
是基于
ooxml-schemas
的低级类构建的。部分原因是由于
apache poi
的中途准备不足,我们需要使用这些类。在下面的示例中,我们需要额外使用
ooxml-schemas-1.4.jar
,因为
apache poi
的
poi-ooxml-schemas-4.0.0.jar
直到现在才包含了
org.openxmlformats.schemas.spreadsheetml.x2006.main.CTTableFormula
。不幸的是,没有关于
ooxml-schemas
的公共文档可供参考。因此,我们需要下载源代码并自己进行
javadoc
说明。
以下示例对我有效,使用的是
apache poi 4.0.0
。如果您在编译或运行过程中遇到问题,可能是由于同时在编译时和/或运行时类路径中存在多个不同版本的
apache poi
jar
。
请勿混合使用不同版本的 apache poi。另外,就像之前所说,我的代码需要
ooxml-schemas-1.4.jar 的所有模式的完整 jar 包。
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 ExcelExpandingTable {
static void addRowToTable(XSSFTable table) {
int lastTableRow = table.getEndCellReference().getRow();
int totalsRowCount = table.getTotalsRowCount();
int lastTableDataRow = lastTableRow - totalsRowCount;
lastTableRow++;
lastTableDataRow++;
AreaReference newTableArea = new AreaReference(
table.getStartCellReference(),
new CellReference(
lastTableRow,
table.getEndCellReference().getCol()
),
SpreadsheetVersion.EXCEL2007
);
AreaReference newTableDataArea = new AreaReference(
table.getStartCellReference(),
new CellReference(
lastTableDataRow,
table.getEndCellReference().getCol()
),
SpreadsheetVersion.EXCEL2007
);
XSSFSheet sheet = table.getXSSFSheet();
if (totalsRowCount > 0) {
sheet.shiftRows(lastTableDataRow, lastTableRow, 1);
for (int r = lastTableDataRow; r < lastTableRow + 1; r++) {
XSSFRow row = sheet.getRow(r);
if (row != null) {
long rRef = row.getCTRow().getR();
for (Cell cell : row) {
String cRef = ((XSSFCell)cell).getCTCell().getR();
((XSSFCell)cell).getCTCell().setR(cRef.replaceAll("[0-9]", "") + rRef);
}
}
}
}
XSSFRow row = sheet.getRow(lastTableDataRow); if (row == null) row = sheet.createRow(lastTableDataRow);
int firstTableCol = table.getStartCellReference().getCol();
for (CTTableColumn tableCol : table.getCTTable().getTableColumns().getTableColumnList()) {
if (tableCol.getCalculatedColumnFormula() != null) {
int id = (int)tableCol.getId();
String formula = tableCol.getCalculatedColumnFormula().getStringValue();
XSSFCell cell = row.getCell(firstTableCol + id - 1); if (cell == null) cell = row.createCell(firstTableCol + id - 1);
cell.setCellFormula(formula);
}
}
table.setArea(newTableArea);
table.getCTTable().getAutoFilter().setRef(newTableDataArea.formatAsString());
table.updateReferences();
}
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).getSheetAt(0);
XSSFTable table = sheet.getTables().get(0);
addRowToTable(table);
workbook.write(out);
}
}
}
上面是2018年的情况,现在已经是2022年了。
使用当前的
apache poi 5.2.2
版本,代码可能如下所示:
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 ExcelExpandingTable {
static XSSFRow addRowToTable(XSSFTable table) {
int lastTableRow = table.getEndCellReference().getRow();
int totalsRowCount = table.getTotalsRowCount();
int lastTableDataRow = lastTableRow - totalsRowCount;
int firstTableCol = table.getStartCellReference().getCol();
lastTableRow++;
lastTableDataRow++;
AreaReference newTableArea = new AreaReference(
table.getStartCellReference(),
new CellReference(
lastTableRow,
table.getEndCellReference().getCol()
),
SpreadsheetVersion.EXCEL2007
);
XSSFSheet sheet = table.getXSSFSheet();
if (totalsRowCount > 0) {
sheet.shiftRows(lastTableDataRow, lastTableRow, 1);
for (CTTableColumn tableCol : table.getCTTable().getTableColumns().getTableColumnList()) {
if (tableCol.getCalculatedColumnFormula() != null) {
int id = (int)tableCol.getId();
String formula = tableCol.getCalculatedColumnFormula().getStringValue();
int rFirst = table.getStartCellReference().getRow() + table.getHeaderRowCount();
int rLast = table.getEndCellReference().getRow() - table.getTotalsRowCount();
int c = table.getStartCellReference().getCol() + id - 1;
sheet.getWorkbook().setCellFormulaValidation(false);
for (int r = rFirst; r <= rLast; r++) {
XSSFRow row = sheet.getRow(r); if (row == null) row = sheet.createRow(r);
XSSFCell cell = row.getCell(c); if (cell == null) cell = row.createCell(c);
cell.setCellFormula(formula);
}
}
}
}
XSSFRow row = sheet.getRow(lastTableDataRow); if (row == null) row = sheet.createRow(lastTableDataRow);
for (CTTableColumn tableCol : table.getCTTable().getTableColumns().getTableColumnList()) {
if (tableCol.getCalculatedColumnFormula() != null) {
int id = (int)tableCol.getId();
String formula = tableCol.getCalculatedColumnFormula().getStringValue();
XSSFCell cell = row.getCell(firstTableCol + id - 1); if (cell == null) cell = row.createCell(firstTableCol + id - 1);
cell.getSheet().getWorkbook().setCellFormulaValidation(false);
cell.setCellFormula(formula);
}
}
row = sheet.getRow(lastTableDataRow); if (row == null) row = sheet.createRow(lastTableDataRow);
XSSFRow rowAbove = sheet.getRow(lastTableDataRow - 1); if (row == null) row = sheet.createRow(lastTableDataRow - 1);
for (CTTableColumn tableCol : table.getCTTable().getTableColumns().getTableColumnList()) {
int id = (int)tableCol.getId();
XSSFCell cellAbove = rowAbove.getCell(firstTableCol + id - 1);
if (cellAbove != null) {
XSSFCellStyle styleAbove = cellAbove.getCellStyle();
XSSFCell cell = row.getCell(firstTableCol + id - 1); if (cell == null) cell = row.createCell(firstTableCol + id - 1);
cell.setCellStyle(styleAbove);
}
}
table.setArea(newTableArea);
table.updateReferences();
return sheet.getRow(lastTableDataRow);
}
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).getSheetAt(0);
XSSFTable table = sheet.getTables().get(0);
XSSFRow row = addRowToTable(table);
workbook.write(out);
}
}
}
根据
常见问题解答,此代码现在需要使用
poi-ooxml-full-5.2.2.jar
而非
ooxml-schemas-1.4.jar
才能运行
apache poi 5.2.2
。一些错误已经得到修复,但是当使用包含表格引用的公式时,使用
XSSFCell.setCellFormula
会出现一个新错误。但是,使用
XSSFWorkbook.setCellFormulaValidation(false)
可以避免这种情况。
AreaReference
构造函数时出现多个问题的原因可能是在编译时和/或运行时类路径中存在多个不同版本的apache poi
jar
。不要混合不同的apache poi
版本。 - Axel Richter