我正在使用 Apache POI 5.2.5 生成 .xlsx 文件,将字节数组发送到客户端并在客户端计算机上本地下载该文件。
尝试打开文件时,我收到“我们发现 *.xlsx 中的某些内容存在问题。您是否希望我们尝试尽可能地恢复它”错误。单击“是”,文件将在 Windows 和 Mac 上打开,并且所有数据都存在,但不是作为表对象。尝试使用谷歌表格打开文件没有问题,并且表格对象有边框。
当我尝试将其直接保存在服务器计算机上时,问题仍然存在,所以我认为问题不在客户端。
生成字节数组的代码:
private byte[] generateExcelBytes(List<DATA_OBJECT> data) {
try (XSSFWorkbook workbook = new XSSFWorkbook()) {
XSSFSheet sheet = workbook.createSheet("Data");
CellStyle headerStyle = workbook.createCellStyle();
Font headerFont = workbook.createFont();
headerFont.setBold(true);
headerFont.setFontHeightInPoints((short) 14);
headerStyle.setFont(headerFont);
CellStyle dataStyle = workbook.createCellStyle();
Font dataFont = workbook.createFont();
dataFont.setFontHeightInPoints((short) 12);
dataStyle.setFont(dataFont);
createTable(sheet, 0, 0, headerStyle, dataStyle, data);
// Write the workbook content to a ByteArrayOutputStream
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
workbook.write(outputStream);
return outputStream.toByteArray();
} catch (IOException e) {
logger.error("Exception when creating xlsx file for account {} CDI status tables", account.getId(), e);
}
return null;
}
private void createTable(XSSFSheet sheet, int startRow, int startCol, CellStyle headerStyle, CellStyle dataStyle, List<DATA_OBJECT> data) {
// Create sample data (replace with your actual data generation logic)
Row headerRow = sheet.createRow(startRow);
// for each header
createCell(headerRow, startCol + X, HEADER_NAME, headerStyle);
// Create data rows
for (int i = 0; i < data.size(); i++) {
DATA_OBJECT data_object = data.get(i);
Row dataRow = sheet.createRow(startRow + i + 1);
// for each data object
createCell(dataRow, startCol, data_object.get_relevant_value, dataStyle);
}
int endRow = startRow + baseHoldings.size();
int endCol = startCol + NUM_OF_COLUMNS;
XSSFAutoFilter autoFilter = sheet.setAutoFilter(new CellRangeAddress(startRow, endRow, startCol, endCol));
for (int i = startCol; i <= startCol + NUM_OF_COLUMNS; i++) {
sheet.autoSizeColumn(i);
sheet.setColumnWidth(i, sheet.getColumnWidth(i) + 1000);
}
// Create Excel table
XSSFTable table = sheet.createTable(null);
CTTable cttable = table.getCTTable();
cttable.setDisplayName("Data Table");
cttable.setId(1);
cttable.setName("Data_Table");
cttable.setRef(new CellRangeAddress(startRow, endRow, startCol, endCol).formatAsString());
}
private void createCell(Row row, int columnIndex, String value, CellStyle style) {
Cell cell = row.createCell(columnIndex);
cell.setCellValue(value);
cell.setCellStyle(style);
}
在 Mac 上打开文件,我发现了一个实际错误:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<recoveryLog xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">
<logFileName>Repair Result to <FILE_NAME>0.xml</logFileName>
<summary>Errors were detected in file '<FILE_PATH>.xlsx'</summary>
<removedParts summary="Following is a list of removed parts:">
<removedPart>Removed Part: /xl/tables/table1.xml part with XML error. (Table) Xml parsing error Line 2, column 70.</removedPart>
</removedParts>
</recoveryLog>
/xl/tables/table1.xml 文件内容:
<?xml version="1.0" encoding="UTF-8"?>
<table id="1" displayName="Data Table" name="Data_Table" ref="A1:H3" xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main"/>
我不知道是什么原因导致XML解析错误,或者在服务器端生成文件时如何避免它。 任何帮助将不胜感激。
这里有多个问题。
请勿在
areaReference
构造函数中使用 null
== XSSFTable
。相反,将表的数据范围定义为 AreaReference
。
避免使用
CTTable
。 Apache POI 5.2.5 提供了 XSSFTable
中的大部分必需功能。在您的情况下,您忘记创建需要与工作表单元格相对应的表列。当 XSSFTable table = sheet.createTable(areaReference)
不是 areaReference
而是正确的 null
时,这是由 AreaReference
正确完成的。
显示名称不得包含空格。
并且表格不能有工作表过滤器。表使用自己的过滤器。不幸的是到目前为止
XSSFTable
还没有提供。因此必须使用 CTTable
:table.getCTTable().addNewAutoFilter().setRef(areaReference.formatAsString());
。
不太确定你的
List<DATA_OBJECT> data
,所以我不太确定你计算的endRow
和endCol
是否正确。当然,表格不能从 startRow
开始,因为表头位于表格之外,不是吗?
使用大部分方法的完整示例,但为了简单起见,使用了
Object[][] data
和结果文件:
import java.io.FileOutputStream;
import java.io.IOException;
import org.apache.poi.ss.SpreadsheetVersion;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.Font;
import org.apache.poi.ss.util.AreaReference;
import org.apache.poi.ss.util.CellReference;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFTable;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.apache.poi.xssf.usermodel.XSSFAutoFilter;
public class CreateTableExample {
static void createTable(XSSFSheet sheet, int startRow, int startCol, CellStyle headerStyle, CellStyle dataStyle, Object[][] data) {
Row headerRow = sheet.createRow(startRow);
// for each header
createCell(headerRow, startCol, "HEADER_NAME", headerStyle);
int headerRows = 1; // how many rows outside table?
int endRow = startRow + headerRows + data.length - 1;
int endCol = startCol + data[0].length - 1;
// Merge cells for Header
sheet.addMergedRegion(new CellRangeAddress(startRow, startRow, startCol, endCol));
// Create data rows
for (int i = 0; i < data.length; i++) {
Object[] data_object = data[i];
Row dataRow = sheet.createRow(startRow + headerRows + i);
int c = startCol;
for (Object value : data_object) {
createCell(dataRow, startCol + c++, value, dataStyle);
}
}
//XSSFAutoFilter autoFilter = sheet.setAutoFilter(new CellRangeAddress(startRow + headerRows, endRow, startCol, endCol)); // No sheet filter for Table!
for (int i = startCol; i <= endCol; i++) {
sheet.autoSizeColumn(i);
sheet.setColumnWidth(i, sheet.getColumnWidth(i) + 1000);
}
// Define the data range for the table
AreaReference areaReference = new AreaReference(new CellReference(startRow + headerRows, startCol), new CellReference(endRow, endCol), SpreadsheetVersion.EXCEL2007);
// Create Excel table
//XSSFTable table = sheet.createTable(null); // do not use areaReference == null!
XSSFTable table = sheet.createTable(areaReference);
// Set the table style
table.getCTTable().addNewTableStyleInfo();
table.getCTTable().getTableStyleInfo().setName("TableStyleLight12");
table.setName("Data Table");
table.setDisplayName("Data_Table"); // display name must not contain spaces!
table.getCTTable().addNewAutoFilter().setRef(areaReference.formatAsString()); // set AutoFilter in table
}
static void createCell(Row row, int columnIndex, Object value, CellStyle style) {
Cell cell = row.createCell(columnIndex);
if (value instanceof String) {
cell.setCellValue((String)value);
} else if (value instanceof Number) {
cell.setCellValue(((Number)value).doubleValue());
}
cell.setCellStyle(style);
}
public static void main(String[] args) {
// Create sample data (replace with your actual data generation logic)
Object[][] data = new Object[][] {
new Object[] {"Name", "Value", "Class", "Amount"},
new Object[] {"Name 1", 123, "Class 1", 123.45},
new Object[] {"Name 2", 456, "Class 2", 456.78},
new Object[] {"Name 3", 789, "Class 3", 789.01},
new Object[] {"Name 4", 123, "Class 4", 123.45},
new Object[] {"Name 5", 456, "Class 5", 456.78},
//...
};
try (Workbook workbook = new XSSFWorkbook()) {
Sheet sheet = workbook.createSheet("Sheet1");
CellStyle headerStyle = workbook.createCellStyle();
Font headerFont = workbook.createFont();
headerFont.setBold(true);
headerFont.setFontHeightInPoints((short) 14);
headerStyle.setFont(headerFont);
CellStyle dataStyle = workbook.createCellStyle();
Font dataFont = workbook.createFont();
dataFont.setFontHeightInPoints((short) 12);
dataStyle.setFont(dataFont);
createTable((XSSFSheet)sheet, 0, 0, headerStyle, dataStyle, data);
// Save the workbook
try (FileOutputStream fileOut = new FileOutputStream("./workbook_with_table.xlsx")) {
workbook.write(fileOut);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}