<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>5.2.3</version>
</dependency>
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import java.io.*;
import java.util.List;
public class ExcelExporter {
/**
* 導出數據到 Excel,支持新建/追加
* @param filePath 文件完整路徑
* @param headers 表頭數組
* @param dataList 數據列表,每行為 Object[]
*/
public static void export(String filePath, String[] headers, List<Object[]> dataList) {
File file = new File(filePath);
Workbook workbook;
Sheet sheet;
int startRow;
try {
if (file.exists()) {
// 1. 文件存在:讀取現有工作簿
try (FileInputStream fis = new FileInputStream(file)) {
workbook = new XSSFWorkbook(fis);
}
sheet = workbook.getSheetAt(0);
startRow = sheet.getLastRowNum() + 1; // 關鍵:獲取最後行號 + 1
System.out.println("文件存在,從第 " + startRow + " 行開始追加");
} else {
// 2. 文件不存在:創建新工作簿
workbook = new XSSFWorkbook();
sheet = workbook.createSheet("訂單數據");
startRow = 0;
// 寫入表頭
Row headerRow = sheet.createRow(0);
for (int i = 0; i < headers.length; i++) {
Cell cell = headerRow.createCell(i);
cell.setCellValue(headers[i]);
// 可選:設置表頭樣式
}
startRow = 1; // 數據從第 1 行開始(第 0 行是表頭)
}
// 3. 追加數據
CellStyle dataStyle = createDataStyle(workbook);
for (int i = 0; i < dataList.size(); i++) {
Row row = sheet.createRow(startRow + i);
Object[] rowData = dataList.get(i);
for (int j = 0; j < rowData.length; j++) {
Cell cell = row.createCell(j);
setCellValue(cell, rowData[j]);
cell.setCellStyle(dataStyle);
}
}
// 4. 自動調整列寬(可選)
for (int i = 0; i < headers.length; i++) {
sheet.autoSizeColumn(i);
}
// 5. 寫入文件(覆蓋寫入,因為 workbook 已包含舊數據)
try (FileOutputStream fos = new FileOutputStream(file)) {
workbook.write(fos);
}
workbook.close();
System.out.println("導出成功:" + filePath);
} catch (IOException e) {
throw new RuntimeException("Excel 導出失敗", e);
}
}
private static CellStyle createDataStyle(Workbook wb) {
CellStyle style = wb.createCellStyle();
style.setAlignment(HorizontalAlignment.CENTER);
style.setVerticalAlignment(VerticalAlignment.CENTER);
return style;
}
private static void setCellValue(Cell cell, Object value) {
if (value == null) {
cell.setCellValue("");
} else if (value instanceof Number) {
cell.setCellValue(((Number) value).doubleValue());
} else if (value instanceof Boolean) {
cell.setCellValue((Boolean) value);
} else {
cell.setCellValue(value.toString());
}
}
}
public class OrderExportDemo {
public static void main(String[] args) {
String filePath = "C:/Users/Admin/Desktop/orders.xlsx";
String[] headers = {"訂單號", "客戶", "金額", "下單時間"};
// 模擬第一次導出
List<Object[]> batch1 = List.of(
new Object[]{"ORD001", "張三", 100.50, "2024-01-15 10:30"},
new Object[]{"ORD002", "李四", 200.00, "2024-01-15 11:00"}
);
ExcelExporter.export(filePath, headers, batch1);
// 模擬第二次導出(追加)
List<Object[]> batch2 = List.of(
new Object[]{"ORD003", "王五", 150.75, "2024-01-15 14:20"}
);
ExcelExporter.export(filePath, headers, batch2);
}
}
| 需求 | 實現關鍵 |
|---|---|
| 判斷文件存在 | file.exists() |
| 讀取現有文件 | new XSSFWorkbook(fis) 從輸入流構建 |
| 獲取最後行號 | sheet.getLastRowNum() 返回最後一行索引(0-based) |
| 追加寫入 | createRow(lastRowNum + 1) 創建新行 |
| 保留原數據 | 讀取 → 內存修改 → 整體覆蓋寫入 |
SXSSFWorkbook(流式寫入,低內存)替代 XSSFWorkbookworkbook.write(fos) 前調用 sheet.flushRows(100) 保留 100 行內存autoSizeColumn 極耗性能,改用預估寬度Apache POI 官方指南:Quick Guide