1. 引言
Apache POI(Poor Obfuscation Implementation)是Apache软件基金会提供的开源Java API,用于处理Microsoft Office格式文件。其中HSSF处理Excel 97-2003(.xls)格式,XSSF处理Excel 2007+(.xlsx)格式,SXSSF则用于处理大数据量的流式写入。
Excel操作在企业开发中的核心应用场景:
- 数据报表导出(销售数据、财务报表、用户列表等)
- 批量数据导入(商品信息、员工档案、订单数据等)
- 数据统计分析后的结果呈现
- 系统间的数据交换与备份
相比EasyExcel、Hutool等封装框架,原始POI提供了最底层的控制能力,适合需要高度定制化的场景。
2. 环境准备
2.1 Maven依赖配置
xml
<dependencies>
<!-- POI核心依赖 -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>5.2.3</version>
</dependency>
<!-- 处理.xlsx格式 -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>5.2.3</version>
</dependency>
<!-- 可选:处理日期工具 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
<version>4.4</version>
</dependency>
</dependencies>
版本说明:
- 建议使用5.x版本,4.x版本存在内存泄漏问题
- 如需支持.xls格式,poi依赖必不可少
- poi-ooxml包含.xmlbeans和ooxml-security等传递依赖
2.2 环境要求
| 组件 | 最低版本 | 推荐版本 |
|---|---|---|
| JDK | 1.8 | 11+ |
| POI | 3.17 | 5.2.3+ |
| Maven | 3.6+ | 3.8+ |
3. Excel导出实现
3.1 创建工作簿结构
java
/**
* 创建Excel工作簿基础结构
*/
public static Workbook createWorkbook() {
// 创建.xlsx格式工作簿
Workbook workbook = new XSSFWorkbook();
return workbook;
}
/**
* 创建工作表并设置标题
*/
public static Sheet createSheet(Workbook workbook, String sheetName) {
Sheet sheet = workbook.createSheet(sheetName);
// 设置列宽(以字符宽度为单位,256为一个字符宽度)
sheet.setColumnWidth(0, 20 * 256); // A列宽度为20个字符
sheet.setColumnWidth(1, 15 * 256); // B列宽度为15个字符
return sheet;
}
/**
* 创建标题行
*/
public static Row createHeaderRow(Sheet sheet, String[] headers) {
Row row = sheet.createRow(0);
for (int i = 0; i < headers.length; i++) {
Cell cell = row.createCell(i);
cell.setCellValue(headers[i]);
}
return row;
}
3.2 单元格样式设置
java
/**
* 创建标题样式(居中、加粗、背景色)
*/
public static CellStyle createHeaderStyle(Workbook workbook) {
CellStyle style = workbook.createCellStyle();
// 水平居中
style.setAlignment(HorizontalAlignment.CENTER);
// 垂直居中
style.setVerticalAlignment(VerticalAlignment.CENTER);
// 设置字体
Font font = workbook.createFont();
font.setFontName("微软雅黑");
font.setFontHeightInPoints((short) 12);
font.setBold(true);
style.setFont(font);
// 设置背景色(淡蓝色)
style.setFillForegroundColor(IndexedColors.PALE_BLUE.getIndex());
style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
// 设置边框
style.setBorderTop(BorderStyle.THIN);
style.setBorderBottom(BorderStyle.THIN);
style.setBorderLeft(BorderStyle.THIN);
style.setBorderRight(BorderStyle.THIN);
return style;
}
/**
* 创建数据样式(左对齐、自动换行)
*/
public static CellStyle createDataStyle(Workbook workbook) {
CellStyle style = workbook.createCellStyle();
style.setAlignment(HorizontalAlignment.LEFT);
style.setVerticalAlignment(VerticalAlignment.CENTER);
style.setWrapText(true); // 自动换行
// 设置边框
style.setBorderTop(BorderStyle.THIN);
style.setBorderBottom(BorderStyle.THIN);
style.setBorderLeft(BorderStyle.THIN);
style.setBorderRight(BorderStyle.THIN);
return style;
}
/**
* 创建数字格式样式(保留两位小数)
*/
public static CellStyle createNumberStyle(Workbook workbook) {
CellStyle style = workbook.createCellStyle();
style.setAlignment(HorizontalAlignment.RIGHT);
// 创建数字格式
DataFormat format = workbook.createDataFormat();
style.setDataFormat(format.getFormat("#,##0.00"));
style.setBorderTop(BorderStyle.THIN);
style.setBorderBottom(BorderStyle.THIN);
style.setBorderLeft(BorderStyle.THIN);
style.setBorderRight(BorderStyle.THIN);
return style;
}
3.3 处理不同数据类型
java
/**
* 设置单元格值(支持多种数据类型)
*/
public static void setCellValue(Cell cell, Object value) {
if (value == null) {
cell.setCellValue("");
return;
}
if (value instanceof String) {
cell.setCellValue((String) value);
} else if (value instanceof Number) {
cell.setCellValue(((Number) value).doubleValue());
} else if (value instanceof Date) {
cell.setCellValue((Date) value);
} else if (value instanceof Boolean) {
cell.setCellValue((Boolean) value);
} else if (value instanceof Calendar) {
cell.setCellValue((Calendar) value);
} else {
cell.setCellValue(value.toString());
}
}
/**
* 设置日期格式单元格
*/
public static void setDateCellValue(Cell cell, Date date, Workbook workbook) {
CellStyle dateStyle = workbook.createCellStyle();
DataFormat format = workbook.createDataFormat();
dateStyle.setDataFormat(format.getFormat("yyyy年mm月dd日"));
cell.setCellStyle(dateStyle);
cell.setCellValue(date);
}
3.4 响应输出流处理
java
/**
* 导出Excel到HTTP响应
* @param response HttpServletResponse对象
* @param workbook Excel工作簿
* @param fileName 导出文件名
*/
public static void exportExcel(HttpServletResponse response,
Workbook workbook,
String fileName) throws IOException {
// 设置响应头
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
response.setCharacterEncoding("UTF-8");
// 处理文件名编码(防止中文乱码)
String encodedFileName = URLEncoder.encode(fileName, "UTF-8")
.replaceAll("\\+", "%20");
response.setHeader("Content-Disposition",
"attachment;filename*=UTF-8''" + encodedFileName + ".xlsx");
// 写入输出流
try (OutputStream out = response.getOutputStream()) {
workbook.write(out);
out.flush();
} finally {
// 关闭工作簿释放资源
if (workbook != null) {
workbook.close();
}
}
}
4. Excel导入实现
4.1 文件上传与输入流处理
java
/**
* 从MultipartFile读取Excel文件
* @param file Spring MVC上传的文件
* @return Excel工作簿对象
*/
public static Workbook readExcel(MultipartFile file) throws IOException {
String fileName = file.getOriginalFilename();
InputStream inputStream = file.getInputStream();
// 根据文件后缀判断版本
if (fileName.endsWith(".xlsx")) {
return new XSSFWorkbook(inputStream);
} else if (fileName.endsWith(".xls")) {
return new HSSFWorkbook(inputStream);
} else {
throw new IllegalArgumentException("不支持的文件格式,仅支持.xlsx和.xls");
}
}
4.2 工作表读取与数据解析
java
/**
* 读取工作表数据(默认读取第一个工作表)
*/
public static List<List<String>> readSheetData(Sheet sheet) {
List<List<String>> dataList = new ArrayList<>();
// 从第一行开始读取(跳过标题行可从1开始)
for (int rowIndex = 0; rowIndex <= sheet.getLastRowNum(); rowIndex++) {
Row row = sheet.getRow(rowIndex);
if (row == null) continue;
List<String> rowData = new ArrayList<>();
// 遍历该行的所有单元格
for (int cellIndex = 0; cellIndex < row.getLastCellNum(); cellIndex++) {
Cell cell = row.getCell(cellIndex);
String cellValue = getCellValueAsString(cell);
rowData.add(cellValue);
}
dataList.add(rowData);
}
return dataList;
}
4.3 单元格数据类型转换
java
/**
* 获取单元格字符串值(处理各种数据类型)
*/
public static String getCellValueAsString(Cell cell) {
if (cell == null) {
return "";
}
// 根据单元格类型进行转换
switch (cell.getCellType()) {
case STRING:
return cell.getStringCellValue().trim();
case NUMERIC:
// 判断是否为日期类型
if (DateUtil.isCellDateFormatted(cell)) {
Date date = cell.getDateCellValue();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
return sdf.format(date);
} else {
// 避免科学计数法和精度丢失
DecimalFormat df = new DecimalFormat("0");
return df.format(cell.getNumericCellValue());
}
case BOOLEAN:
return String.valueOf(cell.getBooleanCellValue());
case FORMULA:
// 处理公式单元格
try {
return String.valueOf(cell.getNumericCellValue());
} catch (IllegalStateException e) {
return cell.getStringCellValue();
}
case BLANK:
return "";
default:
return "";
}
}
/**
* 获取指定类型的单元格值
*/
public static <T> T getCellValue(Cell cell, Class<T> clazz) {
if (cell == null) return null;
if (clazz == String.class) {
return clazz.cast(getCellValueAsString(cell));
} else if (clazz == Integer.class || clazz == int.class) {
return clazz.cast((int) cell.getNumericCellValue());
} else if (clazz == Long.class || clazz == long.class) {
return clazz.cast((long) cell.getNumericCellValue());
} else if (clazz == Double.class || clazz == double.class) {
return clazz.cast(cell.getNumericCellValue());
} else if (clazz == Boolean.class || clazz == boolean.class) {
return clazz.cast(cell.getBooleanCellValue());
} else if (clazz == Date.class) {
return clazz.cast(cell.getDateCellValue());
}
return null;
}
4.4 异常处理与数据验证
java
/**
* 导入时进行数据验证
*/
public static class ValidationResult {
private boolean valid;
private String errorMessage;
private int rowIndex;
private int columnIndex;
// 构造方法、getter和setter省略...
}
/**
* 验证导入数据
*/
public static ValidationResult validateCell(Cell cell, String fieldName,
boolean required,
int max_length) {
ValidationResult result = new ValidationResult();
result.setValid(true);
String value = getCellValueAsString(cell);
// 必填验证
if (required && (value == null || value.isEmpty())) {
result.setValid(false);
result.setErrorMessage(fieldName + "不能为空");
return result;
}
// 长度验证
if (max_length > 0 && value.length() > max_length) {
result.setValid(false);
result.setErrorMessage(fieldName + "长度不能超过" + max_length + "个字符");
return result;
}
// 其他业务验证逻辑...
return result;
}
/**
* 导入Excel并验证数据
*/
public static List<ValidationResult> importExcelWithValidation(
MultipartFile file,
boolean skipHeader) throws IOException {
Workbook workbook = readExcel(file);
Sheet sheet = workbook.getSheetAt(0);
List<ValidationResult> errors = new ArrayList<>();
int startRow = skipHeader ? 1 : 0;
for (int i = startRow; i <= sheet.getLastRowNum(); i++) {
Row row = sheet.getRow(i);
if (row == null) continue;
// 示例:验证第一列(姓名)
Cell nameCell = row.getCell(0);
ValidationResult result = validateCell(nameCell, "姓名", true, 50);
if (!result.isValid()) {
result.setRowIndex(i + 1);
result.setColumnIndex(1);
errors.add(result);
}
// 验证第二列(年龄)
Cell ageCell = row.getCell(1);
if (ageCell != null) {
try {
int age = (int) ageCell.getNumericCellValue();
if (age < 0 || age > 150) {
ValidationResult ageError = new ValidationResult();
ageError.setValid(false);
ageError.setErrorMessage("年龄必须在0-150之间");
ageError.setRowIndex(i + 1);
ageError.setColumnIndex(2);
errors.add(ageError);
}
} catch (Exception e) {
ValidationResult ageError = new ValidationResult();
ageError.setValid(false);
ageError.setErrorMessage("年龄必须是数字");
ageError.setRowIndex(i + 1);
ageError.setColumnIndex(2);
errors.add(ageError);
}
}
}
workbook.close();
return errors;
}
5. 完整示例代码
5.1 Excel导出工具类
java
package com.example.excel.util;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URLEncoder;
import java.util.Date;
import java.util.List;
/**
* Excel导出工具类
*/
public class ExcelExportUtil {
/**
* 导出数据到Excel
* @param headers 表头数组
* @param dataList 数据列表(每行数据为List<Object>)
* @param fileName 文件名
* @param sheetName 工作表名
*/
public static void exportData(HttpServletResponse response,
String[] headers,
List<List<Object>> dataList,
String fileName,
String sheetName) throws IOException {
// 创建工作簿
Workbook workbook = new XSSFWorkbook();
Sheet sheet = workbook.createSheet(sheetName);
// 创建样式
CellStyle headerStyle = createHeaderStyle(workbook);
CellStyle dataStyle = createDataStyle(workbook);
CellStyle numberStyle = createNumberStyle(workbook);
// 创建标题行
Row headerRow = sheet.createRow(0);
for (int i = 0; i < headers.length; i++) {
Cell cell = headerRow.createCell(i);
cell.setCellValue(headers[i]);
cell.setCellStyle(headerStyle);
}
// 填充数据
for (int i = 0; i < dataList.size(); i++) {
Row row = sheet.createRow(i + 1);
List<Object> rowData = dataList.get(i);
for (int j = 0; j < rowData.size(); j++) {
Cell cell = row.createCell(j);
Object value = rowData.get(j);
// 根据数据类型设置样式和值
if (value == null) {
cell.setCellValue("");
cell.setCellStyle(dataStyle);
} else if (value instanceof Number) {
cell.setCellValue(((Number) value).doubleValue());
cell.setCellStyle(numberStyle);
} else if (value instanceof Date) {
CellStyle dateStyle = workbook.createCellStyle();
DataFormat format = workbook.createDataFormat();
dateStyle.setDataFormat(format.getFormat("yyyy-MM-dd"));
dateStyle.setAlignment(HorizontalAlignment.CENTER);
dateStyle.setBorderTop(BorderStyle.THIN);
dateStyle.setBorderBottom(BorderStyle.THIN);
dateStyle.setBorderLeft(BorderStyle.THIN);
dateStyle.setBorderRight(BorderStyle.THIN);
cell.setCellStyle(dateStyle);
cell.setCellValue((Date) value);
} else {
cell.setCellValue(value.toString());
cell.setCellStyle(dataStyle);
}
}
}
// 自动调整列宽
autoSizeColumns(sheet, headers.length);
// 导出文件
exportExcel(response, workbook, fileName);
}
/**
* 自动调整列宽
*/
private static void autoSizeColumns(Sheet sheet, int columnCount) {
for (int i = 0; i < columnCount; i++) {
sheet.autoSizeColumn(i);
// 设置最小列宽,防止过窄
if (sheet.getColumnWidth(i) < 15 * 256) {
sheet.setColumnWidth(i, 15 * 256);
}
// 设置最大列宽,防止过宽
if (sheet.getColumnWidth(i) > 50 * 256) {
sheet.setColumnWidth(i, 50 * 256);
}
}
}
/**
* 创建标题样式
*/
public static CellStyle createHeaderStyle(Workbook workbook) {
CellStyle style = workbook.createCellStyle();
style.setAlignment(HorizontalAlignment.CENTER);
style.setVerticalAlignment(VerticalAlignment.CENTER);
Font font = workbook.createFont();
font.setFontName("微软雅黑");
font.setFontHeightInPoints((short) 12);
font.setBold(true);
style.setFont(font);
style.setFillForegroundColor(IndexedColors.PALE_BLUE.getIndex());
style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
style.setBorderTop(BorderStyle.THIN);
style.setBorderBottom(BorderStyle.THIN);
style.setBorderLeft(BorderStyle.THIN);
style.setBorderRight(BorderStyle.THIN);
return style;
}
/**
* 创建数据样式
*/
public static CellStyle createDataStyle(Workbook workbook) {
CellStyle style = workbook.createCellStyle();
style.setAlignment(HorizontalAlignment.LEFT);
style.setVerticalAlignment(VerticalAlignment.CENTER);
style.setWrapText(true);
style.setBorderTop(BorderStyle.THIN);
style.setBorderBottom(BorderStyle.THIN);
style.setBorderLeft(BorderStyle.THIN);
style.setBorderRight(BorderStyle.THIN);
return style;
}
/**
* 创建数字格式样式
*/
public static CellStyle createNumberStyle(Workbook workbook) {
CellStyle style = workbook.createCellStyle();
style.setAlignment(HorizontalAlignment.RIGHT);
DataFormat format = workbook.createDataFormat();
style.setDataFormat(format.getFormat("#,##0.00"));
style.setBorderTop(BorderStyle.THIN);
style.setBorderBottom(BorderStyle.THIN);
style.setBorderLeft(BorderStyle.THIN);
style.setBorderRight(BorderStyle.THIN);
return style;
}
/**
* 导出Excel到HTTP响应
*/
public static void exportExcel(HttpServletResponse response,
Workbook workbook, String fileName) throws IOException {
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
response.setCharacterEncoding("UTF-8");
String encodedFileName = URLEncoder.encode(fileName, "UTF-8")
.replaceAll("\\+", "%20");
response.setHeader("Content-Disposition",
"attachment;filename*=UTF-8''" + encodedFileName + ".xlsx");
try (OutputStream out = response.getOutputStream()) {
workbook.write(out);
out.flush();
} finally {
if (workbook != null) {
workbook.close();
}
}
}
}
5.2 Excel导入工具类
java
package com.example.excel.util;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.io.InputStream;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
/**
* Excel导入工具类
*/
public class ExcelImportUtil {
/**
* 导入Excel文件并返回数据
* @param file 上传的Excel文件
* @param skipHeader 是否跳过标题行
* @return 数据列表(每行数据为List<String>)
*/
public static List<List<String>> importExcel(MultipartFile file, boolean skipHeader)
throws IOException {
Workbook workbook = readExcel(file);
Sheet sheet = workbook.getSheetAt(0);
List<List<String>> dataList = new ArrayList<>();
int startRow = skipHeader ? 1 : 0;
for (int i = startRow; i <= sheet.getLastRowNum(); i++) {
Row row = sheet.getRow(i);
if (row == null) continue;
List<String> rowData = new ArrayList<>();
for (int j = 0; j < row.getLastCellNum(); j++) {
Cell cell = row.getCell(j);
String value = getCellValueAsString(cell);
rowData.add(value);
}
dataList.add(rowData);
}
workbook.close();
return dataList;
}
/**
* 读取Excel文件
*/
public static Workbook readExcel(MultipartFile file) throws IOException {
String fileName = file.getOriginalFilename();
InputStream inputStream = file.getInputStream();
if (fileName.endsWith(".xlsx")) {
return new XSSFWorkbook(inputStream);
} else if (fileName.endsWith(".xls")) {
return new HSSFWorkbook(inputStream);
} else {
throw new IllegalArgumentException("不支持的文件格式");
}
}
/**
* 获取单元格字符串值
*/
public static String getCellValueAsString(Cell cell) {
if (cell == null) return "";
switch (cell.getCellType()) {
case STRING:
return cell.getStringCellValue().trim();
case NUMERIC:
if (DateUtil.isCellDateFormatted(cell)) {
Date date = cell.getDateCellValue();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
return sdf.format(date);
} else {
java.text.DecimalFormat df = new java.text.DecimalFormat("0");
return df.format(cell.getNumericCellValue());
}
case BOOLEAN:
return String.valueOf(cell.getBooleanCellValue());
case FORMULA:
try {
return String.valueOf(cell.getNumericCellValue());
} catch (IllegalStateException e) {
return cell.getStringCellValue();
}
case BLANK:
return "";
default:
return "";
}
}
}
5.3 Controller使用示例
java
package com.example.excel.controller;
import com.example.excel.util.ExcelExportUtil;
import com.example.excel.util.ExcelImportUtil;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
* Excel操作Controller示例
*/
@RestController
@RequestMapping("/excel")
public class ExcelController {
/**
* 导出示例
*/
@GetMapping("/export")
public void exportUserList(HttpServletResponse response) throws IOException {
// 准备表头
String[] headers = {"用户ID", "用户名", "年龄", "注册日期", "状态"};
// 准备数据
List<List<Object>> dataList = new ArrayList<>();
for (int i = 1; i <= 10; i++) {
List<Object> row = new ArrayList<>();
row.add(i);
row.add("用户" + i);
row.add(20 + i);
row.add(new java.util.Date());
row.add(i % 2 == 0 ? "启用" : "禁用");
dataList.add(row);
}
// 导出
ExcelExportUtil.exportData(response, headers, dataList,
"用户列表", "用户数据");
}
/**
* 导入示例
*/
@PostMapping("/import")
public String importUserList(@RequestParam("file") MultipartFile file) {
try {
List<List<String>> dataList = ExcelImportUtil.importExcel(file, true);
// 处理导入数据
for (List<String> row : dataList) {
System.out.println("导入数据:" + row);
// 数据库操作...
}
return "导入成功,共导入" + dataList.size() + "条数据";
} catch (IOException e) {
e.printStackTrace();
return "导入失败:" + e.getMessage();
}
}
}
6. 常见问题与解决方案
6.1 大文件内存溢出问题
| 问题 | 解决方案 | 实现方式 |
|---|---|---|
| 大文件OOM | 使用SXSSF流式写入 | new SXSSFWorkbook(rowAccessWindowSize) |
| 大文件读取 | 使用事件模型 | XSSF and SAX (Event API) |
| 样式过多 | 复用样式对象 | 避免为每个单元格创建新样式 |
SXSSF流式写入示例:
java
// 设置内存中保留100行,超过后写入临时文件
SXSSFWorkbook workbook = new SXSSFWorkbook(100);
Sheet sheet = workbook.createSheet("大数据导出");
// 写入数据...
for (int i = 0; i < 100000; i++) {
Row row = sheet.createRow(i);
// ...
}
// 清理临时文件
workbook.dispose();
6.2 数字精度丢失问题
java
// 错误写法:可能丢失精度
double value = cell.getNumericCellValue();
// 正确写法:使用BigDecimal
BigDecimal decimal = new BigDecimal(cell.getNumericCellValue())
.setScale(2, RoundingMode.HALF_UP);
6.3 日期格式问题
java
// 判断单元格是否为日期格式
if (DateUtil.isCellDateFormatted(cell)) {
Date date = cell.getDateCellValue();
// 处理日期...
} else {
// 处理数字...
}
6.4 文件名乱码问题
java
// 完整的文件名编码处理
String encodedFileName = URLEncoder.encode(fileName, "UTF-8")
.replaceAll("\\+", "%20");
response.setHeader("Content-Disposition",
"attachment;filename*=UTF-8''" + encodedFileName + ".xlsx");
6.5 版本兼容性
java
// 动态判断文件版本
Workbook workbook = null;
String fileName = file.getOriginalFilename();
if (fileName.endsWith(".xlsx")) {
workbook = new XSSFWorkbook(inputStream);
} else if (fileName.endsWith(".xls")) {
workbook = new HSSFWorkbook(inputStream);
} else {
throw new IllegalArgumentException("不支持的文件格式");
}
7. 总结与扩展
7.1 框架对比
| 特性 | Apache POI | EasyExcel | Hutool |
|---|---|---|---|
| 学习成本 | 高 | 低 | 低 |
| 性能 | 一般 | 优秀 | 一般 |
| 内存占用 | 较高 | 低 | 较高 |
| 功能完整性 | 丰富 | 基础 | 较全 |
| 定制化能力 | 强 | 弱 | 中等 |
选择建议:
- 需要高度定制化样式和功能 → Apache POI
- 处理百万级大数据量 → EasyExcel
- 快速开发、简单场景 → Hutool
7.2 性能优化要点
- 样式复用:避免为每个单元格创建新样式,全局创建并复用
- 流式处理:大数据场景使用SXSSF
- 批量操作:减少频繁的IO操作
- 及时释放:使用try-with-resources确保资源释放
7.3 实际应用场景
- 报表系统:销售报表、财务报表、数据分析结果导出
- 数据迁移:系统间数据交换、历史数据导入
- 后台管理:用户列表、商品管理、订单导出
- 数据分析:业务数据统计、趋势分析报告
7.4 扩展建议
- 结合注解:基于注解实现自动映射(如Excel导入直接转为实体对象)
- 异步处理:大文件导出采用异步+通知机制
- 模板导出:基于预先设计好的Excel模板进行数据填充
- 多Sheet处理:复杂报表使用多Sheet组织数据
总结:Apache POI提供了完整的Excel处理能力,虽然API相对复杂,但在需要精细控制和高度定制化的场景下仍是不二之选。通过合理封装和优化,可以构建稳定高效的Excel处理工具。