業務需求:讀取大型 Excel,按業務規則拆分為多個小文件,每文件保持原始單元格格式(字體、邊框、對齊、欄寬、行高)。本文基於 JExcelApi (jxl) 示範完整實作。
<dependency> <groupId>net.sourceforge.jexcelapi</groupId> <artifactId>jxl</artifactId> <version>2.6.12</version> </dependency>
public class ExcelSplitter {
public static void splitByRule(String inputPath, String outputDir, int rowsPerFile)
throws IOException, BiffException, WriteException, RowsExceededException {
try (Workbook wb = Workbook.getWorkbook(new File(inputPath))) {
Sheet sheet = wb.getSheet(0);
int totalRows = sheet.getRows();
int totalCols = sheet.getColumns();
int fileIndex = 1;
int rowPtr = 0;
while (rowPtr < totalRows) {
// 識別分組邏輯:此處假設第 0 欄為標識欄
Cell flagCell = sheet.getCell(0, rowPtr);
if (flagCell.getType() != CellType.LABEL) {
rowPtr++;
continue;
}
// 建立新工作簿
File outFile = new File(outputDir, "split_" + fileIndex + ".xls");
try (WritableWorkbook wwb = Workbook.createWorkbook(outFile)) {
WritableSheet ws = wwb.createSheet("Sheet1", 0);
// 複製表頭(第 0、1 行)
copyRowWithFormat(sheet, ws, 0, 0, totalCols, true); // 標題行
copyRowWithFormat(sheet, ws, 1, 1, totalCols, false); // 表頭行
// 合併標題行單元格
ws.mergeCells(0, 0, totalCols - 1, 0);
// 設置特定欄寬
ws.setColumnView(6, 10);
ws.setColumnView(7, 45);
ws.setColumnView(8, 27);
// 複製數據行
int writeRow = 2;
while (rowPtr < totalRows) {
Cell cell0 = sheet.getCell(0, rowPtr);
if (cell0.getType() == CellType.NUMBER) {
copyRowWithFormat(sheet, ws, rowPtr, writeRow, totalCols, false);
writeRow++;
rowPtr++;
} else {
break; // 遇到非數據行,結束當前文件
}
}
wwb.write();
fileIndex++;
}
}
}
}
private static void copyRowWithFormat(Sheet src, WritableSheet dest, int srcRow, int destRow,
int cols, boolean isHeader) {
for (int c = 0; c < cols; c++) {
Cell srcCell = src.getCell(c, srcRow);
WritableCellFormat format = createFormat(srcCell, isHeader);
WritableCell destCell = createCell(srcCell, format);
dest.addCell(new jxl.write.Label(c, destRow, srcCell.getContents(), format));
// 複製行高
if (c == 0) dest.setRowView(destRow, src.getRowView(srcRow));
}
}
private static WritableCellFormat createFormat(Cell cell, boolean isHeader) {
WritableFont font = new WritableFont(
WritableFont.createFont("宋體"),
isHeader ? 20 : 12,
isHeader ? WritableFont.BOLD : WritableFont.NO_BOLD,
false
);
WritableCellFormat format = new WritableCellFormat(font);
format.setAlignment(Alignment.CENTRE);
format.setVerticalAlignment(VerticalAlignment.CENTRE);
format.setBorder(Border.ALL, BorderLineStyle.THIN, Colour.BLACK);
return format;
}
}
| 需求 | JExcelApi 實現 |
|---|---|
| 字體/大小/粗體 | WritableFont + WritableCellFormat |
| 水平/垂直居中 | Alignment.CENTRE / VerticalAlignment.CENTRE |
| 邊框樣式/顏色 | Border.ALL, BorderLineStyle.THIN, Colour.BLACK |
| 合併儲存格 | ws.mergeCells(col1, row1, col2, row2) |
| 欄寬/行高 | setColumnView / setRowView |
| 數字/日期格式 | NumberFormat / DateFormat 配合 WritableCellFormat |
| 限制 | 影響 | 替代方案 |
|---|---|---|
| 僅支援 .xls (BIFF8) | 無法處理 .xlsx、超過 65536 行 | Apache POI (HSSF/XSSF) |
| 記憶體佔用高 | 大文件易 OOM | POI SXSSF (流式寫入) |
| 不支援圖表/樞紐分析表 | 複雜報表無法保真 | POI / 商業庫 |
| 維護停滯 | 安全漏洞、JDK 相容性風險 | 遷移至 POI |
// POI 流式寫入大文件 (SXSSF)
try (SXSSFWorkbook wb = new SXSSFWorkbook(100)) { // 保留 100 行在記憶體
Sheet sheet = wb.createSheet();
// ... 建立樣式、寫入數據 ...
try (FileOutputStream fos = new FileOutputStream("output.xlsx")) {
wb.write(fos);
}
wb.dispose(); // 釋放臨時文件
}
WritableCellFormatwwb.write()(需權衡記憶體)setForceFormulaRecalculation(false)JExcelApi 適合簡單 .xls 維護專案。新專案強烈建議直接使用 Apache POI,獲得 .xlsx 支援、流式處理、活躍社群與長期維護保障。