还在写冗余的POI模板代码?Hutool ExcelUtil 一键搞定Excel导入/导出/大数据量,少写90%重复代码
Apache POI 是 Java 操作 Excel 的标准库,但原生 POI 存在致命痛点:创建工作簿/单元格需几十行模板代码、大数据量读写易 OOM、日期/数字格式处理易出错、样式配置繁琐、模板填充逻辑复杂……而 Hutool 的 cn.hutool.poi.excel 模块(核心是 ExcelUtil)正是针对这些痛点的“轻量化封装”——它基于 POI 简化了 90% 的模板代码,无需关注 Workbook/Sheet/Row 的底层操作,一行代码即可实现 Excel 导入导出,还内置大数据量读写、样式定制、模板填充等能力,让 Excel 操作从“繁琐配置”变为“开箱即用”。
本文将从原生 POI 痛点入手,拆解 ExcelUtil 的核心价值,并结合 6 个高频业务场景(简单导出/导入、大数据量读写、样式定制、模板填充、多sheet操作),对比“原生 POI 写法 vs Hutool 写法”,让你用最少的代码搞定复杂 Excel 操作。
一、先搞懂:ExcelUtil 为什么能替代原生 POI 冗余代码?
原生 POI 操作 Excel 时,80% 的代码都是“重复模板”(创建工作簿、遍历行/单元格、处理空值/类型转换),ExcelUtil 把这些模板封装为一行方法,核心优化如下:
| 原生 POI 痛点 | Hutool ExcelUtil 优化方案 |
|---|---|
| 导出需手动创建 Workbook/Sheet/Row | ExcelUtil.write() 一键生成 Excel,自动创建层级结构 |
| 导入需遍历行/单元格+类型转换 | ExcelUtil.read() 一键读取 Excel 到 List/Map,自动类型转换 |
| 大数据量读写易 OOM | BigExcelWriter/BigExcelReader 基于 SAX 流式读写,避免 OOM |
| 样式配置繁琐(字体/颜色/对齐) | ExcelWriter 内置样式方法,一行定制表头/单元格样式 |
| 模板导出需手动填充占位符 | ExcelUtil.fillTemplate() 一键填充 Excel 模板,支持列表/单值填充 |
| 多sheet操作需切换Sheet对象 | ExcelWriter 简化多sheet创建/写入,无需手动切换 |
| 空值/单元格类型异常难处理 | 自动处理空单元格、日期/数字类型转换,避免类型异常 |
简单说:ExcelUtil 不改变 POI 的核心能力,只是把“底层对象创建、遍历、类型转换、样式配置”等冗余操作封装成工具方法,让你聚焦“要导出/导入什么数据”,而非“怎么操作 Excel 底层对象”。
二、6 大实战场景:从“原生 POI 模板代码”到“一行 Hutool 调用”
按“使用频率”排序,每个场景结合真实业务(如订单导出、用户导入、大数据量报表),附对比示例。
1. 简单 Excel 导出(单sheet,列表转Excel)
核心场景:将用户列表、订单列表导出为 Excel(.xlsx/.xls),原生需手动创建 Workbook、Sheet、Row、Cell,代码冗余;ExcelUtil 一行搞定。
原生 POI 写法(约 40 行代码,冗余且易出错):
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import java.io.FileOutputStream;
import java.util.ArrayList;
import java.util.List;
public class NativeExcelWrite {
// 测试实体:用户
static class User {
private Long id;
private String name;
private Integer age;
public User(Long id, String name, Integer age) {this.id = id;this.name = name;this.age = age;}
// getter/setter
public Long getId() {return id;}
public String getName() {return name;}
public Integer getAge() {return age;}
}
public static void main(String[] args) throws Exception {
// 1. 准备数据
List<User> userList = new ArrayList<>();
userList.add(new User(1L, "张三", 25));
userList.add(new User(2L, "李四", 20));
userList.add(new User(3L, "王五", 30));
// 2. 手动创建Workbook、Sheet
Workbook workbook = new XSSFWorkbook();
Sheet sheet = workbook.createSheet("用户列表");
// 3. 创建表头行
Row headerRow = sheet.createRow(0);
headerRow.createCell(0).setCellValue("ID");
headerRow.createCell(1).setCellValue("姓名");
headerRow.createCell(2).setCellValue("年龄");
// 4. 遍历数据,创建数据行
int rowNum = 1;
for (User user : userList) {
Row row = sheet.createRow(rowNum++);
// 手动设置单元格值,处理类型转换
row.createCell(0).setCellValue(user.getId());
row.createCell(1).setCellValue(user.getName());
row.createCell(2).setCellValue(user.getAge());
}
// 5. 写入文件,手动关闭流
try (FileOutputStream fos = new FileOutputStream("user_export.xlsx")) {
workbook.write(fos);
}
workbook.close(); // 手动关闭,避免内存泄漏
}
}
问题:代码冗余(40 行仅实现简单导出);需手动处理流关闭,易泄漏;无空值处理(如 age 为 null 会抛异常);不支持大数据量(数据量>1万行易 OOM)。
Hutool 写法(一行导出,约 10 行代码):
import cn.hutool.poi.excel.ExcelUtil;
import cn.hutool.poi.excel.ExcelWriter;
import java.util.ArrayList;
import java.util.List;
public class HutoolExcelWrite {
static class User {
private Long id;
private String name;
private Integer age;
public User(Long id, String name, Integer age) {this.id = id;this.name = name;this.age = age;}
// getter/setter
public Long getId() {return id;}
public String getName() {return name;}
public Integer getAge() {return age;}
}
public static void main(String[] args) {
// 1. 准备数据
List<User> userList = new ArrayList<>();
userList.add(new User(1L, "张三", 25));
userList.add(new User(2L, "李四", 20));
userList.add(new User(3L, "王五", 30));
// 2. 一键导出:自动创建Workbook/Sheet,自动处理类型/空值,自动关闭流
try (ExcelWriter writer = ExcelUtil.getWriter("user_export.xlsx")) {
// 写入数据(表头自动从User字段名生成,也可自定义)
writer.write(userList, true);
} // 自动关闭writer,无需手动关闭流
}
}
核心优势:
- 自动创建层级:无需手动创建 Workbook/Sheet/Row,ExcelUtil 自动处理;
- 空值安全:字段为 null 时单元格为空,不抛异常;
- 自动关流:实现 AutoCloseable,try-with-resources 自动关闭,避免内存泄漏;
- 表头自定义:
writer.addHeaderAlias("id", "用户ID")可将表头“id”改为“用户ID”。
业务场景:接口返回用户列表导出为 Excel(如运营后台的“用户导出”功能)。
2. 简单 Excel 导入(读取 Excel 到列表)
核心场景:读取 Excel 文件中的用户数据到 List,原生需遍历行/单元格,手动转换类型;ExcelUtil 一键读取。
原生 POI 写法(约 35 行代码,类型转换繁琐):
import org.apache.poi.ss.usermodel.*;
import java.io.FileInputStream;
import java.util.ArrayList;
import java.util.List;
public class NativeExcelRead {
static class User {
private Long id;
private String name;
private Integer age;
// 构造器、getter/setter
public User(Long id, String name, Integer age) {this.id = id;this.name = name;this.age = age;}
}
public static void main(String[] args) throws Exception {
List<User> userList = new ArrayList<>();
// 1. 手动创建Workbook,读取文件
try (FileInputStream fis = new FileInputStream("user_export.xlsx");
Workbook workbook = WorkbookFactory.create(fis)) {
// 2. 获取第一个Sheet
Sheet sheet = workbook.getSheetAt(0);
// 3. 遍历行(跳过表头行)
for (int rowNum = 1; rowNum <= sheet.getLastRowNum(); rowNum++) {
Row row = sheet.getRow(rowNum);
if (row == null) continue;
// 4. 手动读取单元格,转换类型(易出错)
Long id = (long) row.getCell(0).getNumericCellValue();
String name = row.getCell(1).getStringCellValue();
Integer age = (int) row.getCell(2).getNumericCellValue();
// 5. 添加到列表
userList.add(new User(id, name, age));
}
}
System.out.println("读取到用户数:" + userList.size());
}
}
问题:类型转换繁琐(数字转 Long/Integer 易出错);空单元格处理易抛异常;日期/字符串混合单元格需手动判断类型。
Hutool 写法(一行读取,约 8 行代码):
import cn.hutool.poi.excel.ExcelUtil;
import cn.hutool.poi.excel.ExcelReader;
import java.util.List;
public class HutoolExcelRead {
static class User {
private Long id;
private String name;
private Integer age;
// 无参构造器(必须)、getter/setter
public User() {}
// getter/setter
public Long getId() {return id;}
public void setId(Long id) {this.id = id;}
public String getName() {return name;}
public void setName(String name) {this.name = name;}
public Integer getAge() {return age;}
public void setAge(Integer age) {this.age = age;}
}
public static void main(String[] args) {
// 1. 一键读取:自动跳过表头,转换为User列表,自动处理类型/空值
try (ExcelReader reader = ExcelUtil.getReader("user_export.xlsx")) {
// 读取所有数据到User列表(表头匹配字段名)
List<User> userList = reader.readAll(User.class);
System.out.println("读取到用户数:" + userList.size());
}
}
}
核心优势:
- 自动类型转换:数字→Long/Integer、日期→Date/LocalDateTime,无需手动处理;
- 表头匹配:自动将表头(如“用户ID”)与实体字段(如 id)映射(可通过
addHeaderAlias自定义); - 空值处理:空单元格对应字段为 null,不抛异常;
- 灵活读取:支持读取指定行/列、读取为 Map 列表(无需实体类)。
业务场景:运营上传 Excel 批量导入用户数据(如“批量新增用户”功能)。
3. 大数据量 Excel 读写(避免 OOM,支持 10 万+ 行)
核心场景:导出/导入 10 万+ 行订单数据,原生 POI 因加载全部数据到内存易 OOM;ExcelUtil 的 BigExcelWriter/BigExcelReader 基于 SAX 流式读写,仅加载当前行数据,避免 OOM。
原生 POI 写法(需手动实现 SAX 解析,约 50 行代码):
// 原生POI大数据量导出(XSSFWorkbook 易OOM,需用 SXSSFWorkbook)
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import org.apache.poi.ss.usermodel.*;
import java.io.FileOutputStream;
import java.util.ArrayList;
import java.util.List;
public class NativeBigExcelWrite {
static class Order {
private Long id;
private String orderNo;
private Double amount;
public Order(Long id, String orderNo, Double amount) {this.id = id;this.orderNo = orderNo;this.amount = amount;}
// getter/setter
public Long getId() {return id;}
public String getOrderNo() {return orderNo;}
public Double getAmount() {return amount;}
}
public static void main(String[] args) throws Exception {
// 模拟10万条订单数据
List<Order> orderList = new ArrayList<>();
for (int i = 0; i < 100000; i++) {
orderList.add(new Order((long) i, "ORDER" + i, 100.0 + i));
}
// 1. 创建SXSSFWorkbook(流式写入,仅缓存100行)
SXSSFWorkbook workbook = new SXSSFWorkbook(100); // 超出100行自动刷盘
Sheet sheet = workbook.createSheet("订单列表");
// 2. 创建表头
Row headerRow = sheet.createRow(0);
headerRow.createCell(0).setCellValue("订单ID");
headerRow.createCell(1).setCellValue("订单号");
headerRow.createCell(2).setCellValue("金额");
// 3. 流式写入数据
int rowNum = 1;
for (Order order : orderList) {
Row row = sheet.createRow(rowNum++);
row.createCell(0).setCellValue(order.getId());
row.createCell(1).setCellValue(order.getOrderNo());
row.createCell(2).setCellValue(order.getAmount());
}
// 4. 写入文件,手动刷盘/关闭
try (FileOutputStream fos = new FileOutputStream("big_order.xlsx")) {
workbook.write(fos);
}
workbook.dispose(); // 释放临时文件
workbook.close();
}
}
问题:需手动使用 SXSSFWorkbook,配置刷盘行数;代码冗余;导入需手动实现 SAX 解析,复杂度极高。
Hutool 写法(一键流式读写,约 12 行代码):
import cn.hutool.poi.excel.BigExcelWriter;
import cn.hutool.poi.excel.ExcelUtil;
import java.util.ArrayList;
import java.util.List;
public class HutoolBigExcelWrite {
static class Order {
private Long id;
private String orderNo;
private Double amount;
public Order(Long id, String orderNo, Double amount) {this.id = id;this.orderNo = orderNo;this.amount = amount;}
// getter/setter
public Long getId() {return id;}
public String getOrderNo() {return orderNo;}
public Double getAmount() {return amount;}
}
public static void main(String[] args) {
// 模拟10万条订单数据
List<Order> orderList = new ArrayList<>();
for (int i = 0; i < 100000; i++) {
orderList.add(new Order((long) i, "ORDER" + i, 100.0 + i));
}
// 1. 创建BigExcelWriter(流式写入,自动避免OOM)
try (BigExcelWriter writer = ExcelUtil.getBigWriter("big_order.xlsx")) {
// 自定义表头
writer.addHeaderAlias("id", "订单ID");
writer.addHeaderAlias("orderNo", "订单号");
writer.addHeaderAlias("amount", "金额");
// 2. 流式写入数据(自动刷盘,仅缓存少量行)
writer.write(orderList, true);
} // 自动释放资源,无需手动dispose
}
}
核心优势:
- 无需手动配置 SXSSFWorkbook:
BigExcelWriter内置流式写入,自动处理刷盘; - 低内存占用:仅加载当前行数据,10 万+ 行数据内存占用 < 100MB;
- 导入同理:
BigExcelReader流式读取,一行代码读取大数据量 Excel。
业务场景:财务报表导出(10 万+ 行交易记录)、大数据量订单导入。
4. 带样式的 Excel 导出(自定义表头/单元格样式)
核心场景:导出的 Excel 需要定制样式(如表头红色、居中、加粗,金额列保留 2 位小数),原生 POI 样式配置繁琐;ExcelUtil 一行定制样式。
原生 POI 写法(样式配置冗余,约 40 行代码):
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import java.io.FileOutputStream;
import java.util.ArrayList;
import java.util.List;
public class NativeExcelStyle {
static class User {
private Long id;
private String name;
private Double score;
public User(Long id, String name, Double score) {this.id = id;this.name = name;this.score = score;}
// getter/setter
public Long getId() {return id;}
public String getName() {return name;}
public Double getScore() {return score;}
}
public static void main(String[] args) throws Exception {
List<User> userList = new ArrayList<>();
userList.add(new User(1L, "张三", 95.5));
userList.add(new User(2L, "李四", 88.0));
Workbook workbook = new XSSFWorkbook();
Sheet sheet = workbook.createSheet("成绩表");
// 1. 自定义表头样式(加粗、红色、居中)
CellStyle headerStyle = workbook.createCellStyle();
Font headerFont = workbook.createFont();
headerFont.setBold(true);
headerFont.setColor(Font.COLOR_RED);
headerStyle.setFont(headerFont);
headerStyle.setAlignment(HorizontalAlignment.CENTER);
// 2. 创建表头
Row headerRow = sheet.createRow(0);
Cell cell0 = headerRow.createCell(0);
cell0.setCellValue("用户ID");
cell0.setCellStyle(headerStyle);
Cell cell1 = headerRow.createCell(1);
cell1.setCellValue("姓名");
cell1.setCellStyle(headerStyle);
Cell cell2 = headerRow.createCell(2);
cell2.setCellValue("成绩");
cell2.setCellStyle(headerStyle);
// 3. 自定义数据样式(成绩列保留2位小数)
CellStyle dataStyle = workbook.createCellStyle();
DataFormat format = workbook.createDataFormat();
dataStyle.setDataFormat(format.getFormat("0.00"));
// 4. 写入数据
int rowNum = 1;
for (User user : userList) {
Row row = sheet.createRow(rowNum++);
row.createCell(0).setCellValue(user.getId());
row.createCell(1).setCellValue(user.getName());
Cell scoreCell = row.createCell(2);
scoreCell.setCellValue(user.getScore());
scoreCell.setCellStyle(dataStyle); // 应用小数样式
}
// 5. 写入文件
try (FileOutputStream fos = new FileOutputStream("score_style.xlsx")) {
workbook.write(fos);
}
workbook.close();
}
}
问题:样式配置代码占比 70%;每个单元格需手动应用样式;样式对象创建过多易导致 Excel 体积过大。
Hutool 写法(一行定制样式,约 15 行代码):
import cn.hutool.poi.excel.ExcelUtil;
import cn.hutool.poi.excel.ExcelWriter;
import cn.hutool.poi.excel.style.StyleUtil;
import org.apache.poi.ss.usermodel.*;
import java.util.ArrayList;
import java.util.List;
public class HutoolExcelStyle {
static class User {
private Long id;
private String name;
private Double score;
public User(Long id, String name, Double score) {this.id = id;this.name = name;this.score = score;}
// getter/setter
public Long getId() {return id;}
public String getName() {return name;}
public Double getScore() {return score;}
}
public static void main(String[] args) {
List<User> userList = new ArrayList<>();
userList.add(new User(1L, "张三", 95.5));
userList.add(new User(2L, "李四", 88.0));
try (ExcelWriter writer = ExcelUtil.getWriter("score_style.xlsx")) {
// 1. 自定义表头样式(加粗、红色、居中)
CellStyle headerStyle = StyleUtil.createHeadStyle(writer.getWorkbook());
headerStyle.getFont().setColor(Font.COLOR_RED); // 表头文字红色
writer.setHeaderStyle(headerStyle);
// 2. 自定义成绩列样式(保留2位小数)
CellStyle scoreStyle = StyleUtil.createCellStyle(writer.getWorkbook());
scoreStyle.setDataFormat(writer.getWorkbook().createDataFormat().getFormat("0.00"));
writer.setStyle(2, scoreStyle); // 第3列(索引2)应用小数样式
// 3. 自定义表头别名
writer.addHeaderAlias("id", "用户ID");
writer.addHeaderAlias("name", "姓名");
writer.addHeaderAlias("score", "成绩");
// 4. 写入数据
writer.write(userList, true);
}
}
}
核心优势:
- 样式工具简化:
StyleUtil内置常用样式(表头样式、居中样式),无需手动创建 Font/CellStyle; - 批量应用样式:
setStyle(columnIndex, style)给整列应用样式,无需逐个单元格设置; - 样式复用:ExcelUtil 自动复用样式对象,减少 Excel 体积。
业务场景:财务报表导出(金额列保留 2 位小数、表头高亮)、成绩表导出(及格/不及格标色)。
5. 模板导出(基于 Excel 模板填充数据)
核心场景:基于预设的 Excel 模板(含占位符如 ${name}、列表占位符 {{$list}})填充数据,原生需手动查找占位符单元格并替换;ExcelUtil 一键填充。
模板文件(template.xlsx)内容:
| 字段 | 值 |
|---|---|
| 报表名称 | ${reportName} |
| 生成时间 | ${createTime} |
| 序号 | 姓名 |
| {{$list}} | {{name}} |
原生 POI 写法(手动查找占位符,约 40 行代码):
import org.apache.poi.ss.usermodel.*;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
public class NativeExcelTemplate {
static class User {
private String name;
private Integer age;
public User(String name, Integer age) {this.name = name;this.age = age;}
// getter/setter
public String getName() {return name;}
public Integer getAge() {return age;}
}
public static void main(String[] args) throws Exception {
// 1. 读取模板文件
try (FileInputStream fis = new FileInputStream("template.xlsx");
Workbook workbook = WorkbookFactory.create(fis)) {
Sheet sheet = workbook.getSheetAt(0);
// 2. 填充单值占位符
for (Row row : sheet) {
for (Cell cell : row) {
if (cell.getCellType() == CellType.STRING) {
String value = cell.getStringCellValue();
if ("${reportName}".equals(value)) {
cell.setCellValue("用户报表");
} else if ("${createTime}".equals(value)) {
cell.setCellValue(new Date().toString());
}
}
}
}
// 3. 填充列表占位符(需手动插入行,复杂度极高)
List<User> userList = new ArrayList<>();
userList.add(new User("张三", 25));
userList.add(new User("李四", 20));
// 省略:手动查找{{$list}}位置,插入行,替换{{name}}/{{age}}...
// 4. 写入文件
try (FileOutputStream fos = new FileOutputStream("template_result.xlsx")) {
workbook.write(fos);
}
}
}
}
问题:列表占位符填充需手动插入行,逻辑复杂;占位符匹配易遗漏;不支持复杂数据结构(如嵌套对象)。
Hutool 写法(一键填充模板,约 10 行代码):
import cn.hutool.core.date.DateUtil;
import cn.hutool.poi.excel.ExcelUtil;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class HutoolExcelTemplate {
static class User {
private String name;
private Integer age;
public User(String name, Integer age) {this.name = name;this.age = age;}
// getter/setter
public String getName() {return name;}
public Integer getAge() {return age;}
}
public static void main(String[] args) {
// 1. 准备数据:单值+列表
Map<String, Object> data = new HashMap<>();
data.put("reportName", "用户报表");
data.put("createTime", DateUtil.now());
List<User> userList = new ArrayList<>();
userList.add(new User("张三", 25));
userList.add(new User("李四", 20));
data.put("list", userList);
// 2. 一键填充模板,生成结果文件
ExcelUtil.fillTemplate("template.xlsx", data, "template_result.xlsx");
}
}
核心优势:
- 自动匹配占位符:支持
${单值}、{{$list}}列表占位符,无需手动查找; - 支持复杂数据:列表元素可为实体类/Map,自动匹配
{{字段名}}; - 无需手动操作行:自动插入列表行,处理行高/样式继承。
业务场景:合同模板填充、报表模板导出(固定格式+动态数据)。
6. 多 Sheet 导出/导入
核心场景:导出/导入包含多个 Sheet 的 Excel(如“用户表”+“订单表”),原生需手动切换 Sheet;ExcelUtil 简化多 Sheet 操作。
Hutool 写法(多 Sheet 导出,约 15 行代码):
import cn.hutool.poi.excel.ExcelUtil;
import cn.hutool.poi.excel.ExcelWriter;
import java.util.ArrayList;
import java.util.List;
public class HutoolMultiSheet {
static class User {private Long id;private String name;public User(Long id, String name) {this.id = id;this.name = name;}public Long getId() {return id;}public String getName() {return name;}}
static class Order {private Long id;private String orderNo;public Order(Long id, String orderNo) {this.id = id;this.orderNo = orderNo;}public Long getId() {return id;}public String getOrderNo() {return orderNo;}}
public static void main(String[] args) {
// 准备数据
List<User> userList = new ArrayList<>();
userList.add(new User(1L, "张三"));
List<Order> orderList = new ArrayList<>();
orderList.add(new Order(1L, "ORDER1"));
try (ExcelWriter writer = ExcelUtil.getWriter("multi_sheet.xlsx")) {
// 1. 写入第一个Sheet(用户表)
writer.setSheet("用户表");
writer.addHeaderAlias("id", "用户ID");
writer.write(userList, true);
// 2. 切换到第二个Sheet(订单表)
writer.setSheet("订单表");
writer.addHeaderAlias("id", "订单ID");
writer.write(orderList, true);
}
// 多Sheet导入:读取指定Sheet
try (ExcelReader reader = ExcelUtil.getReader("multi_sheet.xlsx")) {
// 读取“用户表”Sheet
List<User> users = reader.setSheet("用户表").readAll(User.class);
// 读取“订单表”Sheet
List<Order> orders = reader.setSheet("订单表").readAll(Order.class);
System.out.println("用户数:" + users.size() + ",订单数:" + orders.size());
}
}
}
核心优势:
- 一键切换 Sheet:
setSheet(name/index)无需手动创建/获取 Sheet; - 样式复用:不同 Sheet 可复用样式配置;
- 导入灵活:支持按名称/索引读取指定 Sheet。
业务场景:综合报表导出(包含多个维度的数据 Sheet)。
三、避坑指南:这 6 个细节 90% 的人会踩
1. 大数据量导出未用 BigExcelWriter 导致 OOM
- 误区:用
ExcelUtil.getWriter()导出 10 万+ 行数据,以为自动处理 OOM; - 真相:普通
ExcelWriter基于 XSSFWorkbook,大数据量需用ExcelUtil.getBigWriter(); - 解决:数据量 > 1000 行时,优先使用
BigExcelWriter。
2. 导入实体类缺少无参构造器导致失败
- 误区:导入到实体类时,实体类只有有参构造器,读取失败;
- 真相:ExcelUtil 通过反射创建实体,必须有无参构造器(手动写或用 Lombok
@NoArgsConstructor); - 解决:给导入的实体类添加无参构造器。
3. 日期格式解析异常
- 误区:Excel 中的日期单元格读取为数字/字符串,转换失败;
- 解决:① 手动指定日期格式:
reader.setDateFormat("yyyy-MM-dd");② 实体类日期字段用Date/LocalDateTime。
4. 模板导出占位符匹配失败
- 误区:模板中的占位符如
${reportName}有空格(如${ reportName }),导致匹配不到; - 真相:占位符前后不能有空格,需严格匹配
${字段名}/{{字段名}}; - 解决:清理模板占位符的多余空格。
5. 流未关闭导致文件被占用
- 误区:未用 try-with-resources 包裹 ExcelWriter/Reader,导致文件句柄泄漏;
- 解决:所有 ExcelWriter/Reader 都放在 try-with-resources 中,自动关闭流。
6. 表头别名与字段名不匹配
- 误区:
addHeaderAlias("userId", "用户ID"),但实体字段是id,导致数据为空; - 真相:
addHeaderAlias(实体字段名, 表头名),顺序不能反; - 解决:确认别名顺序为“字段名→表头名”,如
addHeaderAlias("id", "用户ID")。
四、核心方法速查表(按场景分类)
| 场景 | 工具类方法 | 示例 |
|---|---|---|
| 简单 Excel 导出 | ExcelUtil.getWriter() | ExcelUtil.getWriter("file.xlsx").write(list, true) |
| 大数据量导出 | ExcelUtil.getBigWriter() | ExcelUtil.getBigWriter("big_file.xlsx").write(list, true) |
| 简单 Excel 导入 | ExcelUtil.getReader() | ExcelUtil.getReader("file.xlsx").readAll(User.class) |
| 大数据量导入 | ExcelUtil.getBigReader() | ExcelUtil.getBigReader("big_file.xlsx").readRow(rowHandler) |
| 模板导出 | ExcelUtil.fillTemplate() | ExcelUtil.fillTemplate("template.xlsx", data, "result.xlsx") |
| 多 Sheet 切换 | ExcelWriter/Reader.setSheet() | writer.setSheet("用户表") |
| 表头别名自定义 | ExcelWriter.addHeaderAlias() | writer.addHeaderAlias("id", "用户ID") |
| 样式定制 | StyleUtil.createHeadStyle() | writer.setHeaderStyle(StyleUtil.createHeadStyle(workbook)) |
总结
Hutool ExcelUtil 的核心价值是**“封装 POI 的底层模板代码,解决 Excel 操作的 OOM、类型转换、样式配置、模板填充等痛点”**,它让 Excel 操作从“几十行模板代码”简化为“一行核心调用”,同时兼容原生 POI 的所有能力(如复杂样式、自定义解析)。
核心用法记住 3 点:
- 导出:小数据量用
ExcelUtil.getWriter(),大数据量用getBigWriter(); - 导入:实体类必须有无参构造器,日期字段指定格式;
- 模板:占位符严格匹配,列表用
{{$list}}+{{字段名}}。
更多推荐
所有评论(0)