Apache POI 是 Java 操作 Microsoft Office 文件的標準庫,廣泛應用於報表導出、數據導入等場景。本文詳細講解如何使用 POI 生成 Excel 文件,並重點解決圖片導出的定位與縮放難題。
POI 中 Excel 文件層級結構:
public class ErrorCondition {
private String name; // 姓名
private String idCard; // 身份證
private String status; // 錯誤狀態
private String message; // 錯誤信息
// 建構子、getter/setter 省略
}
public class ExportErrorExcel {
public static void main(String[] args) {
// 1. 建立工作簿
HSSFWorkbook wb = new HSSFWorkbook();
// 2. 建立工作表
HSSFSheet sheet = wb.createSheet("身份證錯誤信息");
// 3. 建立表頭樣式
HSSFCellStyle headerStyle = wb.createCellStyle();
headerStyle.setAlignment(HSSFCellStyle.ALIGN_CENTER);
headerStyle.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex());
headerStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
// 4. 寫入表頭
HSSFRow headerRow = sheet.createRow(0);
String[] headers = {"姓名", "身份證", "錯誤狀態", "錯誤信息"};
for (int i = 0; i < headers.length; i++) {
HSSFCell cell = headerRow.createCell(i);
cell.setCellValue(headers[i]);
cell.setCellStyle(headerStyle);
}
// 5. 寫入數據
List<ErrorCondition> list = getErrorData();
for (int i = 0; i < list.size(); i++) {
ErrorCondition data = list.get(i);
HSSFRow row = sheet.createRow(i + 1);
row.createCell(0).setCellValue(data.getName());
row.createCell(1).setCellValue(data.getIdCard());
row.createCell(2).setCellValue(data.getStatus());
row.createCell(3).setCellValue(data.getMessage());
}
// 6. 自動調整列寬
for (int i = 0; i < headers.length; i++) {
sheet.autoSizeColumn(i);
}
// 7. 寫入文件
try (FileOutputStream fos = new FileOutputStream("D:/errorCondition.xls")) {
wb.write(fos);
System.out.println("Excel 文件生成成功");
} catch (IOException e) {
e.printStackTrace();
}
}
private static List<ErrorCondition> getErrorData() {
// 測試數據...
}
}
HSSFClientAnchor anchor = new HSSFClientAnchor(
dx1, dy1, dx2, dy2, // 起始/終止儲存格內的相對座標 (0-1023, 0-255)
col1, row1, // 起始儲存格 (列, 行)
col2, row2 // 終止儲存格 (列, 行)
);
參數說明:
dx1, dy1:圖片左上角在起始儲存格內的偏移量dx2, dy2:圖片右下角在終止儲存格內的偏移量col1, row1:左上角所在儲存格(0 基索引)col2, row2:右下角所在儲存格anchor.setAnchorType(3):移動且不調整大小(預設 0:移動並調整大小)public class ExcelImageExport {
public static void main(String[] args) {
try (FileOutputStream fos = new FileOutputStream("D:/test_image.xls")) {
HSSFWorkbook wb = new HSSFWorkbook();
HSSFSheet sheet = wb.createSheet("圖片測試");
// 讀取圖片轉為位元組陣列
BufferedImage img = ImageIO.read(new File("F:/photo.jpg"));
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ImageIO.write(img, "jpg", baos);
byte[] imgBytes = baos.toByteArray();
// 獲取繪圖管理器
HSSFPatriarch patriarch = sheet.createDrawingPatriarch();
// 設定錨點:跨儲存格 B2 到 F9
HSSFClientAnchor anchor = new HSSFClientAnchor(0, 0, 1023, 255, 1, 1, 5, 8);
anchor.setAnchorType(ClientAnchor.AnchorType.MOVE_DONT_RESIZE);
// 導出圖片
int pictureIdx = wb.addPicture(imgBytes, HSSFWorkbook.PICTURE_TYPE_JPEG);
patriarch.createPicture(anchor, pictureIdx);
wb.write(fos);
System.out.println("圖片導出成功");
} catch (Exception e) {
e.printStackTrace();
}
}
}
// 建立多個錨點,控制不同位置 HSSFClientAnchor anchor1 = new HSSFClientAnchor(0, 0, 1023, 255, 1, 1, 5, 8); // 第 1-8 行 HSSFClientAnchor anchor2 = new HSSFClientAnchor(0, 0, 1023, 255, 1, 9, 5, 16); // 第 9-16 行 patriarch.createPicture(anchor1, pictureIdx); patriarch.createPicture(anchor2, pictureIdx);
// 獲取圖片原始尺寸 BufferedImage img = ImageIO.read(new File(path)); int imgWidth = img.getWidth(); int imgHeight = img.getHeight(); // 計算縮放比例,適應儲存格 double scale = Math.min((double)cellWidth / imgWidth, (double)cellHeight / imgHeight); // 設置錨點 dx2, dy2 實現縮放 anchor.setDx2((int)(1023 * scale)); anchor.setDy2((int)(255 * scale));
// 使用 SXSSF (流式處理),僅保留最近 100 行在記憶體
SXSSFWorkbook wb = new SXSSFWorkbook(100);
SXSSFSheet sheet = wb.createSheet("大數據導出");
// 後續操作與 HSSF 相同
// 寫入後需手動釋放臨時文件
wb.dispose();
| 問題 | 原因 | 解決方案 |
|---|---|---|
| 圖片顯示為紅叉 | 圖片格式不支援或路徑錯誤 | 確認使用 JPEG/PNG,檢查文件路徑 |
| 圖片位置偏移 | 錨點參數理解錯誤 | 仔細核對 col1/row1/col2/row2 對應關係 |
| 導出文件打不開 | Workbook 未正確 close | 使用 try-with-resources 確保資源釋放 |
| OOM 記憶體溢出 | 大量數據載入記憶體 | 使用 SXSSF 流式寫入 |
掌握 POI 的核心在於理解 Workbook → Sheet → Row → Cell 的層級關係,以及 Patriarch + Anchor + Picture 的圖片導出三要素。實際專案中建議封裝通用的導出工具類,支援模板導出、樣式複用、大數據流式處理等企業級需求。