从基础到进阶:打造通用Excel导出工具的完整指南
在后端开发中,Excel导出是一项几乎每个系统都必备的功能。无论是数据报表、信息归档还是统计分析,都需要将系统中的数据以直观的Excel格式呈现给用户。本文将详细介绍如何基于Apache POI实现一个通用的Excel导出工具,并通过样式优化提升导出文件的专业性,让你无需重复开发即可满足各类导出需求。
一、为什么需要通用Excel导出工具?
在传统开发模式中,为每个业务模块编写Excel导出代码会导致以下问题:
- 重复劳动:创建工作簿、构建表头、填充数据等逻辑在不同模块反复编写
- 维护困难:当需要调整格式时,需修改多处代码,易遗漏且效率低下
- 风格混乱:不同开发者实现的导出功能样式不一,影响用户体验
通用Excel导出工具的核心价值在于:通过反射机制实现任意实体类的一键导出,一次开发,全项目复用,同时支持灵活的样式定制,兼顾功能性与美观度。
二、基础版实现:快速搭建导出功能
基础版工具类专注于实现核心导出逻辑,通过反射自动识别实体类字段,生成表头和数据行,满足最基本的导出需求。
1. 核心代码实现
import io.swagger.annotations.ApiModelProperty;
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.xssf.usermodel.XSSFWorkbook;
import java.io.ByteArrayOutputStream;
import java.lang.reflect.Field;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
public class ExcelExportUtil {
/**
* 基础版Excel导出
* @param entityList 待导出的数据集合
* @param clazz 实体类字节码(用于反射获取字段信息)
* @return 导出的Excel字节数组
*/
public static <T> byte[] outputExcel(List<T> entityList, Class<T> clazz) {
try {
if (entityList == null || entityList.isEmpty()) return null;
// 1. 创建工作簿和工作表
Workbook workbook = new XSSFWorkbook();
Sheet sheet = workbook.createSheet("Sheet1");
// 2. 获取字段与表头的映射关系(从Swagger注解提取)
Map<Field, String> fieldTitleMap = getFieldTitleMap(clazz.getDeclaredFields());
// 3. 创建表头行
Row titleRow = sheet.createRow(0);
titleRow.createCell(0).setCellValue("序号"); // 固定序号列
int cellIndex = 1;
for (Field field : fieldTitleMap.keySet()) {
titleRow.createCell(cellIndex).setCellValue(fieldTitleMap.get(field));
cellIndex++;
}
// 4. 填充数据行
for (int i = 0; i < entityList.size(); i++) {
T entity = entityList.get(i);
processDataRow(sheet, fieldTitleMap, i + 1, entity);
}
// 5. 转换为字节数组返回
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
workbook.write(byteArrayOutputStream);
byte[] buffer = byteArrayOutputStream.toByteArray();
byteArrayOutputStream.close();
return buffer;
} catch (Exception e) {
throw new RuntimeException("导出Excel失败:" + e);
}
}
/**
* 从实体类字段的@ApiModelProperty注解中提取表头名称
*/
private static <T> Map<Field, String> getFieldTitleMap(Field[] fieldList) {
Map<Field, String> fieldTitleMap = new LinkedHashMap<>(); // 保持字段声明顺序
for (Field field : fieldList) {
ApiModelProperty apiModelProperty = field.getAnnotation(ApiModelProperty.class);
if (apiModelProperty != null) {
fieldTitleMap.put(field, apiModelProperty.value());
}
}
return fieldTitleMap;
}
/**
* 处理单条数据,填充到Excel行
*/
private static <T> void processDataRow(Sheet sheet, Map<Field, String> fieldTitleMap, int rowIndex, T entity) {
try {
Row dataRow = sheet.createRow(rowIndex);
dataRow.createCell(0).