alilibaba 开源的读写 excel 比较好用的工具类,相较之前常用的 POI 使用方式更简便,且防止了 OOM 产生
Github 项目地址:https://github.com/alibaba/easyexcel
POM 依赖
<!-- https://mvnrepository.com/artifact/com.alibaba/easyexcel -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>2.2.7</version>
</dependency>
写入 Excel
/** 根据实体类自动生成表头*/
@Data
// 忽略所有没有添加注释的字段
@ExcelIgnoreUnannotated
public class DemoData {
// 其中的值就是表头
@ExcelProperty("字符串")
private String string;
@ExcelProperty("日期")
private Date date;
@ExcelProperty("数字")
private Double doubleData;
// 忽略这个字段
@ExcelIgnore
private String ignore;
}
// 写入 Excel
public void simpleWrite() {
String fileName = "路径//....xlsx";
/** 写法一 */
// 指定用哪个 class 去写,写到第一个 sheet 并命名为模板。文件流会自动关闭(导出为 03 版 excel 需传入 excelType 参数)
EasyExcel.write(fileName, DemoData.class).sheet("模板").doWrite(data());
/** 写法二(两种写法效果完全一样)*/
ExcelWriter excelWriter = null;
try {
excelWriter = EasyExcel.write(fileName, DemoData.class).build();
WriteSheet writeSheet = EasyExcel.writerSheet("模板").build();
excelWriter.write(data(), writeSheet);
} finally {
// 手动关闭流
if (excelWriter != null) {
excelWriter.finish();
}
}
}
写入 Excel 时做数据转换
// 自定义转换器
import com.alibaba.excel.converters.Converter;
import com.alibaba.excel.converters.WriteConverterContext;
import com.alibaba.excel.enums.CellDataTypeEnum;
import com.alibaba.excel.metadata.data.WriteCellData;
import java.util.HashMap;
import java.util.Map;
public class UserIdConverter implements Converter<Integer> {
// 自定义的转出规则 map
private static Map<Integer, String> converRule = new HashMap<>();
static {
converRule.put(2, "哈啊啊");
converRule.put(3, "哈呀呀");
}
@Override
public Class<?> supportJavaTypeKey() {
// 实体类中属性的类
return Integer.class;
}
@Override
public CellDataTypeEnum supportExcelTypeKey() {
// Excel 中数据对应的类
return CellDataTypeEnum.STRING;
}
/**
* 将实体类属性值转换为数据表中的数据
*
* @param value 实体类属性值
* @return 包含了数据表中转换后的数据
*/
@Override
public WriteCellData<?> convertToExcelData(Integer value, ExcelContentProperty contentProperty
, GlobalConfiguration globalConfiguration) {
return new WriteCellData<>(converRule.get(value) == null ? "" : converRule.get(value));
}
}
// 在实体类属性值上指定转换器
@ExcelProperty(value = "用户ID", converter = UserIdConverter.class)
private Integer id;
读取 Excel
读取一个 Excel 并将其中的所有数据存储到数据库中
- 编写监听器
// DemoDataListener 不能被 spring 管理,每次读取 excel 时都要 new ,里面用到 bean 时可以通过构造方法传进去
@Slf4j
@Data
public class DemoDataListener extends AnalysisEventListener<DemoData> {
// 每隔 5 条存储数据库,实际使用中可以 3000 条,然后清理 list ,方便内存回收
private static final int BATCH_COUNT = 5;
// 定义数据存储 list
List<DemoData> list = new ArrayList<DemoData>();
// 通过构造方法将这个被 Spring 管理的类对象传进来并赋值
private DemoDAO demoDAO;
public DemoDataListener(DemoDAO demoDAO) {
this.demoDAO = demoDAO;
}
/**
* 存储数据库的方法
*/
private void saveData() {
log.info("{} 条数据,开始存储数据库!", list.size());
demoDAO.save(list);
log.info("存储数据库成功!");
}
/**
* excel 表中的每一条数据解析时都会来调用 invoke 方法,通过表头名和属性名对应上来构造数据
*/
@Override
public void invoke(DemoData data, AnalysisContext context) {
log.info("解析到一条数据:{}", data);
list.add(data);
// 达到 BATCH_COUNT 时去存储一次数据库,防止过多数据在内存造成 OOM
if (list.size() >= BATCH_COUNT) {
saveData();
// 存储完成后清理 list
list.clear();
}
}
/**
* 所有数据都解析完成后会来调用
*/
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
// 确保最后不满 BATCH_COUNT 条数的数据也存储到数据库
saveData();
log.info("所有数据存储完毕!");
}
}
- 程序中读取 excel
DemoDataListener demoDataListener = new DemoDataListener();
EasyExcel.read(file, demoDataListener).sheet().headRowNumber(0).doRead();