Excel 导入导出工具类详解:基于 Spring 和 Apache POI 的通用解决方案
本文分享一套功能强大的 Excel 导入导出工具类,基于 Spring 框架和 Apache POI 实现,支持注解驱动的字段映射、数据类型自动转换、样式定制等高级功能。
工具类核心功能
- 智能导入:支持 Excel 数据到 Java 对象的自动转换
- 灵活导出:Java 对象到 Excel 的自动导出
- 注解驱动:通过注解控制字段映射行为
- 类型转换:自动处理常见数据类型(日期、数值、布尔值等)
- 错误处理:导入时跳过错误行,导出时标记错误字段
核心工具类源码
1. Excel 导入工具类 (ExcelImportUtil.java)
java
package com.xk.toolkit.util.file.excel;
import com.xk.toolkit.util.file.excel.property.ExcelIgnore;
import com.xk.toolkit.util.file.excel.property.ExcelProperty;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.*;
/**
* Excel导入工具类
*
* <p>提供通用的Excel导入功能,支持基于注解的字段映射和类型转换</p>
*
* <p>功能特点:
* <ul>
* <li>基于反射机制实现通用导入</li>
* <li>支持字段过滤(@ExcelIgnore)</li>
* <li>支持自定义列名和日期格式(@ExcelProperty)</li>
* <li>自动类型转换(字符串、数值、日期、布尔值)</li>
* <li>公式单元格处理</li>
* <li>智能列名匹配(支持大小写不敏感匹配)</li>
* <li>容错处理(跳过空行和错误行)</li>
* </ul>
*/
public class ExcelImportUtil {
/**
* 通用Excel导入方法
*
* @param <T> 数据类型泛型
* @param file 上传的Excel文件
* @param clazz 目标对象类型
* @return 导入成功的数据对象列表
* @throws IOException 当文件解析失败时抛出
*
* <p>执行流程:
* <ol>
* <li>获取字段映射关系</li>
* <li>解析Excel文件</li>
* <li>构建列索引到字段的映射</li>
* <li>遍历数据行并转换为对象</li>
* <li>类型转换和字段赋值</li>
* <li>返回成功导入的对象列表</li>
* </ol>
*/
public static <T> List<T> importExcel(MultipartFile file, Class<T> clazz) throws IOException {
List<T> resultList = new ArrayList<>();
// 获取字段映射(列名->字段对象)
Map<String, Field> fieldMap = getFieldMap(clazz);
// 使用try-with-resources确保流关闭
try (InputStream inputStream = file.getInputStream();
// 创建XSSFWorkbook解析.xlsx格式
Workbook workbook = new XSSFWorkbook(inputStream)) {
// 获取第一个工作表
Sheet sheet = workbook.getSheetAt(0);
// 获取表头行(第0行)
Row headerRow = sheet.getRow(0);
// 列索引到字段的映射
Map<Integer, FieldMapping> columnMapping = new HashMap<>();
// 1. 构建列映射关系 =======================================
for (int col = 0; col < headerRow.getLastCellNum(); col++) {
Cell cell = headerRow.getCell(col);
if (cell != null) {
// 获取列名并去除空格
String columnName = cell.getStringCellValue().trim();
// 根据列名查找对应字段
Field field = findFieldByColumnName(fieldMap, columnName);
if (field != null) {
// 存储映射关系:列索引 -> (字段对象, 日期格式)
columnMapping.put(col, new FieldMapping(field, getDateFormat(field)));
}
}
}
// 2. 处理数据行 ==========================================
for (int rowNum = 1; rowNum <= sheet.getLastRowNum(); rowNum++) {
Row row = sheet.getRow(rowNum);
// 跳过空行
if (row == null) continue;
try {
// 通过反射创建对象实例
T obj = clazz.getDeclaredConstructor().newInstance();
// 标记行是否有有效数据
boolean hasData = false;
// 遍历所有列
for (int col = 0; col < headerRow.getLastCellNum(); col++) {
Cell cell = row.getCell(col);
// 跳过空单元格
if (cell == null) continue;
// 获取列映射信息
FieldMapping mapping = columnMapping.get(col);
if (mapping != null) {
// 解析单元格值(根据目标类型和日期格式)
Object value = parseCellValue(cell, mapping.field.getType(), mapping.dateFormat);
if (value != null) {
// 设置字段可访问(私有字段)
mapping.field.setAccessible(true);
// 给字段赋值
mapping.field.set(obj, value);
// 标记有有效数据
hasData = true;
}
}
}
// 如果有有效数据则添加到结果列表
if (hasData) {
resultList.add(obj);
}
} catch (Exception e) {
// 记录错误行信息(实际应用中应使用日志框架)
System.err.printf("导入第 %d 行数据出错: %s%n", rowNum + 1, e.getMessage());
}
}
} catch (Exception e) {
throw new IOException("文件解析失败: " + e.getMessage(), e);
}
return resultList;
}
/**
* 获取字段映射关系
*
* @param clazz 目标类
* @return 列名到字段对象的映射Map
*
* <p>映射规则:
* <ul>
* <li>忽略带@ExcelIgnore注解的字段</li>
* <li>优先使用@ExcelProperty的value作为键</li>
* <li>无注解时使用字段名作为键</li>
* </ul>
*/
private static Map<String, Field> getFieldMap(Class<?> clazz) {
Map<String, Field> fieldMap = new HashMap<>();
// 遍历所有声明字段
for (Field field : clazz.getDeclaredFields()) {
// 跳过忽略字段
if (field.isAnnotationPresent(ExcelIgnore.class)) continue;
// 获取ExcelProperty注解
ExcelProperty annotation = field.getAnnotation(ExcelProperty.class);
// 确定映射键:注解值优先,否则使用字段名
String key = (annotation != null && !annotation.value().isEmpty()) ?
annotation.value() : field.getName();
fieldMap.put(key, field);
}
return fieldMap;
}
/**
* 根据列名查找字段
*
* @param fieldMap 字段映射表
* @param columnName Excel列名
* @return 匹配的字段对象,未找到返回null
*
* <p>匹配策略:
* <ol>
* <li>精确匹配(区分大小写)</li>
* <li>忽略大小写匹配</li>
* </ol>
*/
private static Field findFieldByColumnName(Map<String, Field> fieldMap, String columnName) {
// 1. 精确匹配
if (fieldMap.containsKey(columnName)) {
return fieldMap.get(columnName);
}
// 2. 忽略大小写匹配
for (String key : fieldMap.keySet()) {
if (key.equalsIgnoreCase(columnName)) {
return fieldMap.get(key);
}
}
return null;
}
/**
* 获取字段的日期格式
*
* @param field 目标字段
* @return 日期格式字符串(默认"yyyy-MM-dd HH:mm:ss")
*/
private static String getDateFormat(Field field) {
ExcelProperty annotation = field.getAnnotation(ExcelProperty.class);
// 优先使用注解指定的格式,否则使用默认格式
return (annotation != null) ? annotation.dateFormat() : "yyyy-MM-dd HH:mm:ss";
}
/**
* 解析单元格值
*
* @param cell 单元格对象
* @param targetType 目标Java类型
* @param dateFormat 日期格式(仅对日期类型有效)
* @return 转换后的Java对象
*
* <p>处理逻辑:
* <ul>
* <li>字符串类型 -> convertStringValue()</li>
* <li>数值类型 -> 判断是否为日期</li>
* <li>布尔类型 -> 直接返回</li>
* <li>公式类型 -> 解析公式结果</li>
* </ul>
*/
private static Object parseCellValue(Cell cell, Class<?> targetType, String dateFormat) {
// 根据单元格类型分发处理
switch (cell.getCellType()) {
case STRING:
// 字符串类型处理
return convertStringValue(cell.getStringCellValue().trim(), targetType, dateFormat);
case NUMERIC:
// 数值类型:判断是否为日期格式
if (DateUtil.isCellDateFormatted(cell)) {
// 日期类型处理
return convertDateValue(cell.getDateCellValue(), targetType);
} else {
// 普通数值处理
return convertNumericValue(cell.getNumericCellValue(), targetType);
}
case BOOLEAN:
// 布尔类型直接返回
return cell.getBooleanCellValue();
case FORMULA:
// 公式单元格特殊处理
return parseFormulaCell(cell, targetType, dateFormat);
default:
// 其他类型返回null
return null;
}
}
/**
* 解析公式单元格
*
* @param cell 公式单元格
* @param targetType 目标Java类型
* @param dateFormat 日期格式
* @return 公式计算结果的转换值
*/
private static Object parseFormulaCell(Cell cell, Class<?> targetType, String dateFormat) {
try {
// 根据公式结果类型处理
switch (cell.getCachedFormulaResultType()) {
case STRING:
return convertStringValue(cell.getStringCellValue().trim(), targetType, dateFormat);
case NUMERIC:
if (DateUtil.isCellDateFormatted(cell)) {
return convertDateValue(cell.getDateCellValue(), targetType);
} else {
return convertNumericValue(cell.getNumericCellValue(), targetType);
}
case BOOLEAN:
return cell.getBooleanCellValue();
default:
return null;
}
} catch (Exception e) {
// 公式解析异常返回null
return null;
}
}
/**
* 转换字符串值到目标类型
*
* @param value 字符串值
* @param targetType 目标Java类型
* @param dateFormat 日期格式
* @return 转换后的Java对象
* @throws IllegalArgumentException 当转换失败时抛出
*
* <p>支持转换类型:
* <ul>
* <li>String</li>
* <li>Integer/int</li>
* <li>Long/long</li>
* <li>Double/double</li>
* <li>Boolean/boolean(支持"是/否"、"YES/NO"、"TRUE/FALSE"、"1/0")</li>
* <li>LocalDate(按指定格式)</li>
* <li>LocalDateTime(按指定格式)</li>
* </ul>
*/
private static Object convertStringValue(String value, Class<?> targetType, String dateFormat) {
if (value.isEmpty()) return null;
try {
// 字符串类型直接返回
if (targetType == String.class) return value;
// 整型转换
if (targetType == Integer.class || targetType == int.class) return Integer.parseInt(value);
// 长整型转换
if (targetType == Long.class || targetType == long.class) return Long.parseLong(value);
// 双精度转换
if (targetType == Double.class || targetType == double.class) return Double.parseDouble(value);
// 布尔类型智能转换
if (targetType == Boolean.class || targetType == boolean.class) {
return "是".equals(value) || "YES".equalsIgnoreCase(value) ||
"TRUE".equalsIgnoreCase(value) || "1".equals(value);
}
// 日期转换
if (targetType == LocalDate.class) {
return LocalDate.parse(value, DateTimeFormatter.ofPattern(dateFormat));
}
// 日期时间转换
if (targetType == LocalDateTime.class) {
return LocalDateTime.parse(value, DateTimeFormatter.ofPattern(dateFormat));
}
} catch (Exception e) {
throw new IllegalArgumentException("值转换失败: " + value + " -> " + targetType.getSimpleName());
}
// 未匹配类型返回原始字符串
return value;
}
/**
* 转换数值到目标类型
*
* @param value 数值
* @param targetType 目标Java类型
* @return 转换后的Java对象
*/
private static Object convertNumericValue(double value, Class<?> targetType) {
// 整型转换
if (targetType == Integer.class || targetType == int.class) return (int) value;
// 长整型转换
if (targetType == Long.class || targetType == long.class) return (long) value;
// 双精度转换
if (targetType == Double.class || targetType == double.class) return value;
// 单精度转换
if (targetType == Float.class || targetType == float.class) return (float) value;
// 布尔转换(>0为true)
if (targetType == Boolean.class || targetType == boolean.class) return value > 0;
// 默认返回原始值
return value;
}
/**
* 转换Date对象到目标类型
*
* @param date Java Date对象
* @param targetType 目标Java类型
* @return 转换后的时间对象
*/
private static Object convertDateValue(Date date, Class<?> targetType) {
if (date == null) return null;
// Date类型直接返回
if (targetType == Date.class) return date;
// 转换为LocalDateTime
if (targetType == LocalDateTime.class) {
return date.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
}
// 转换为LocalDate
if (targetType == LocalDate.class) {
return date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
}
// 默认返回Date
return date;
}
/**
* 字段映射辅助类
*
* <p>封装字段和对应的日期格式信息</p>
*/
private static class FieldMapping {
// 目标字段
final Field field;
// 日期格式(来自@ExcelProperty注解)
final String dateFormat;
FieldMapping(Field field, String dateFormat) {
this.field = field;
this.dateFormat = dateFormat;
}
}
}
2. Excel 导出工具类 (ExcelExportUtil.java)
java
package com.xk.toolkit.util.file.excel;
import com.mnsn.framework.law.util.property.ExcelIgnore;
import com.mnsn.framework.law.util.property.ExcelProperty;
import jakarta.servlet.http.HttpServletResponse;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import java.io.IOException;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Field;
import java.net.URLEncoder;
import java.util.List;
/**
* Excel导出工具类
*
* <p>提供通用的Excel导出功能,支持基于注解的字段映射和样式定制</p>
*
* <p>功能特点:
* <ul>
* <li>基于反射机制实现通用导出</li>
* <li>支持字段过滤(@ExcelIgnore)</li>
* <li>支持自定义列名(@ExcelProperty)</li>
* <li>自动设置表头和数据样式</li>
* <li>自动调整列宽适应内容</li>
* <li>支持常见数据类型(数值、字符串、布尔值)</li>
* <li>响应流直接输出,避免内存溢出</li>
* </ul>
*/
public class ExcelExportUtil {
/**
* 通用Excel导出方法
*
* @param <T> 数据类型泛型
* @param response Http响应对象,用于输出Excel文件流
* @param dataList 要导出的数据列表
* @param fileName 导出文件名(不含扩展名)
* @param sheetName 工作表名称
* @param clazz 数据对象类型(用于反射获取字段信息)
* @throws IOException 当文件导出失败时抛出
*
* <p>执行流程:
* <ol>
* <li>创建工作簿和工作表</li>
* <li>创建带样式的表头行</li>
* <li>遍历数据列表填充数据行</li>
* <li>自动调整列宽</li>
* <li>设置响应头并输出Excel文件流</li>
* </ol>
*/
public static <T> void exportExcel(HttpServletResponse response,
List<T> dataList,
String fileName,
String sheetName,
Class<T> clazz) throws IOException {
// 使用XSSFWorkbook创建.xlsx格式的Excel工作簿(支持大文件)
try (Workbook workbook = new XSSFWorkbook()) {
// 创建工作表
Sheet sheet = workbook.createSheet(sheetName);
// 获取所有声明的字段(包括私有字段)
Field[] fields = clazz.getDeclaredFields();
// 创建表头行(第0行)
Row headerRow = sheet.createRow(0);
// 创建表头样式
CellStyle headerStyle = createHeaderStyle(workbook);
// 列索引计数器
int colNum = 0;
// 遍历所有字段
for (Field field : fields) {
// 设置可访问私有字段
field.setAccessible(true);
// 检查是否忽略该字段
if (!field.isAnnotationPresent(ExcelIgnore.class)) {
// 获取列名(优先使用注解值,无注解则使用字段名)
String headerName = getHeaderName(field);
// 创建表头单元格
Cell cell = headerRow.createCell(colNum++);
// 设置表头文本
cell.setCellValue(headerName);
// 应用表头样式
cell.setCellStyle(headerStyle);
}
}
// 创建数据单元格样式
CellStyle dataStyle = createDataStyle(workbook);
// 行索引计数器(从第1行开始)
int rowNum = 1;
// 遍历数据列表
for (T data : dataList) {
// 创建数据行
Row row = sheet.createRow(rowNum++);
// 重置列索引
colNum = 0;
// 遍历所有字段
for (Field field : fields) {
field.setAccessible(true);
// 检查是否忽略该字段
if (!field.isAnnotationPresent(ExcelIgnore.class)) {
Cell cell;
try {
// 通过反射获取字段值
Object value = field.get(data);
// 创建数据单元格
cell = row.createCell(colNum++);
// 设置单元格值(根据类型处理)
setCellValue(cell, value);
// 应用数据样式
cell.setCellStyle(dataStyle);
} catch (IllegalAccessException e) {
// 字段访问异常处理
cell = row.createCell(colNum++);
cell.setCellValue("[ERROR]");
}
}
}
}
// 自动调整列宽(根据内容)
for (int i = 0; i < colNum; i++) {
sheet.autoSizeColumn(i);
}
// 设置响应内容类型为Excel
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
response.setCharacterEncoding("utf-8");
// 设置下载文件名(UTF-8编码处理中文)
response.setHeader("Content-Disposition", "attachment; filename=" +
URLEncoder.encode(fileName + ".xlsx", "UTF-8"));
// 将工作簿写入响应输出流
workbook.write(response.getOutputStream());
}
}
/**
* 创建表头样式
*
* @param workbook Excel工作簿对象
* @return 配置好的表头单元格样式
*
* <p>样式特点:
* <ul>
* <li>深蓝色背景</li>
* <li>白色粗体文字</li>
* <li>居中对齐</li>
* </ul>
*/
private static CellStyle createHeaderStyle(Workbook workbook) {
// 创建单元格样式
CellStyle style = workbook.createCellStyle();
// 创建字体
Font font = workbook.createFont();
// 设置粗体
font.setBold(true);
// 设置白色字体
font.setColor(IndexedColors.WHITE.getIndex());
// 应用字体
style.setFont(font);
// 设置深蓝色背景
style.setFillForegroundColor(IndexedColors.DARK_BLUE.getIndex());
// 设置实心填充
style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
// 设置水平居中
style.setAlignment(HorizontalAlignment.CENTER);
return style;
}
/**
* 创建数据行样式
*
* @param workbook Excel工作簿对象
* @return 配置好的数据单元格样式
*
* <p>样式特点:
* <ul>
* <li>四周边框</li>
* <li>无特殊背景色</li>
* </ul>
*/
private static CellStyle createDataStyle(Workbook workbook) {
CellStyle style = workbook.createCellStyle();
// 设置上边框(细线)
style.setBorderTop(BorderStyle.THIN);
// 设置下边框(细线)
style.setBorderBottom(BorderStyle.THIN);
// 设置左边框(细线)
style.setBorderLeft(BorderStyle.THIN);
// 设置右边框(细线)
style.setBorderRight(BorderStyle.THIN);
return style;
}
/**
* 获取字段的列名
*
* @param field 实体类字段
* @return 列名字符串(优先返回注解值)
*/
private static String getHeaderName(Field field) {
// 检查是否有ExcelProperty注解
if (field.isAnnotationPresent(ExcelProperty.class)) {
// 返回注解的value值
return field.getAnnotation(ExcelProperty.class).value();
}
// 默认返回字段名
return field.getName();
}
/**
* 设置单元格值(根据Java类型处理)
*
* @param cell 目标单元格
* @param value 要设置的值
*
* <p>处理规则:
* <ul>
* <li>null值 -> 空字符串</li>
* <li>数值类型 -> 转换为Double</li>
* <li>布尔类型 -> 直接设置</li>
* <li>其他类型 -> toString()结果</li>
* </ul>
*/
private static void setCellValue(Cell cell, Object value) {
if (value == null) {
cell.setCellValue(""); // 空值处理
} else if (value instanceof Number) {
// 数值类型转换为Double
cell.setCellValue(((Number) value).doubleValue());
} else if (value instanceof Boolean) {
// 布尔类型直接设置
cell.setCellValue((Boolean) value);
} else {
// 其他类型使用字符串表示
cell.setCellValue(value.toString());
}
}
}
3. 注解系统
Excel 属性注解 (ExcelProperty.java)
java
package com.xk.toolkit.util.file.excel.property;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
// 列映射注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface ExcelProperty {
// 列名
String value() default "";
// 日期格式(仅对时间类型有效)
String dateFormat() default "yyyy-MM-dd HH:mm:ss";
}
Excel 忽略注解 (ExcelIgnore.java)
java
package com.xk.toolkit.util.file.excel.property;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
// 忽略字段注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface ExcelIgnore {
}
Excel 列宽注解 (ExcelColumnWidth.java)
java
package com.xk.toolkit.util.file.excel.property;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
// ExcelColumnWidth.java
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface ExcelColumnWidth {
int value() default 20; // 列宽(字符数)
}
使用示例
1. 定义实体类
java
public class Employee {
@ExcelProperty("员工ID")
private Long id;
@ExcelProperty("员工姓名")
private String name;
@ExcelProperty(value = "入职日期", dateFormat = "yyyy年MM月dd日")
private LocalDate hireDate;
@ExcelProperty("薪资")
private Double salary;
@ExcelProperty("是否在职")
private Boolean active;
@ExcelIgnore
private String password;
// 省略 getter/setter
}
2. 导入 Excel 数据
java
@PostMapping("/import")
public ResponseEntity<String> importEmployees(@RequestParam("file") MultipartFile file) {
try {
List<Employee> employees = ExcelImportUtil.importExcel(file, Employee.class);
employeeService.saveAll(employees);
return ResponseEntity.ok("成功导入 " + employees.size() + " 条数据");
} catch (IOException e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body("导入失败: " + e.getMessage());
}
}
3. 导出 Excel 数据
java
@GetMapping("/export")
public void exportEmployees(HttpServletResponse response) throws IOException {
List<Employee> employees = employeeService.getAllEmployees();
ExcelExportUtil.exportExcel(
response,
employees,
"员工数据报表",
"员工信息",
Employee.class
);
}
技术要点解析
-
智能类型转换:
- 字符串自动转换为目标类型(数值、日期、布尔值)
- 支持多种布尔值表示形式(是/否、YES/NO、TRUE/FALSE、1/0)
- 日期格式自定义(通过注解)
-
容错处理:
- 导入时自动跳过空行
- 错误行记录到控制台(生产环境应替换为日志框架)
- 导出时错误字段标记为"[ERROR]"
-
性能优化:
- 使用 try-with-resources 确保资源关闭
- 反射字段缓存提高性能
- 流式输出避免内存溢出
-
扩展性:
- 支持自定义样式(修改 createHeaderStyle/createDataStyle 方法)
- 支持自定义列宽(通过 ExcelColumnWidth 注解)
- 支持多种数据类型扩展
依赖配置
xml
<!-- Spring Boot Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>3.2.0</version>
</dependency>
<!-- Apache POI -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>5.2.5</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>5.2.5</version>
</dependency>
<!-- Jakarta EE (兼容 Spring Boot 3.x) -->
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
<version>6.0.0</version>
<scope>provided</scope>
</dependency>
总结
本文分享的 Excel 导入导出工具类具有以下优势:
- 开箱即用:简单注解配置即可实现复杂导入导出功能
- 高度可定制:支持自定义列名、日期格式、列宽等
- 智能类型转换:自动处理常见数据类型转换
- 企业级健壮性:完善的错误处理和容错机制
- 性能优化:适合处理中小规模数据(<10万行)
在实际项目中,您可以根据业务需求进一步扩展功能,例如:
- 添加数据校验逻辑
- 支持多Sheet页导出
- 添加导出进度提示
- 集成数据字典转换
希望这套工具类能帮助您提升开发效率,欢迎在评论区交流使用心得!