什么是EasyExcel
EasyExcel是一个由阿里巴巴开源的Java库,专注于处理大数据量的Excel文件读写操作。它以高性能、低内存占用和简单易用著称,特别适合处理大规模数据。EasyExcel通过异步处理和NIO技术优化了读写性能,同时提供了丰富的API和注解支持,使得数据导入导出变得简单快捷。
怎么使用EasyExcel
导入依赖
<!-- https://github.com/alibaba/easyexcel -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>3.1.1</version>
</dependency>
导入excel示例
被导入的实体类
@Getter
@Setter
@EqualsAndHashCode
public class DemoData {
private String string;
private Date date;
private Double doubleData;
}
监听器
// 有个很重要的点 DemoDataListener 不能被spring管理,要每次读取excel都要new,然后里面用到spring可以构造方法传进去
@Slf4j
public class DemoDataListener implements ReadListener<DemoData> {
/**
* 每隔5条存储数据库,实际使用中可以100条,然后清理list ,方便内存回收
*/
private static final int BATCH_COUNT = 100;
/**
* 缓存的数据
*/
private List<DemoData> cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
/**
* 假设这个是一个DAO,当然有业务逻辑这个也可以是一个service。当然如果不用存储这个对象没用。
*/
private DemoDAO demoDAO;
public DemoDataListener() {
// 这里是demo,所以随便new一个。实际使用如果到了spring,请使用下面的有参构造函数
demoDAO = new DemoDAO();
}
/**
* 如果使用了spring,请使用这个构造方法。每次创建Listener的时候需要把spring管理的类传进来
*
* @param demoDAO
*/
public DemoDataListener(DemoDAO demoDAO) {
this.demoDAO = demoDAO;
}
/**
* 这个每一条数据解析都会来调用
*
* @param data one row value. Is is same as {@link AnalysisContext#readRowHolder()}
* @param context
*/
@Override
public void invoke(DemoData data, AnalysisContext context) {
log.info("解析到一条数据:{}", JSON.toJSONString(data));
cachedDataList.add(data);
// 达到BATCH_COUNT了,需要去存储一次数据库,防止数据几万条数据在内存,容易OOM
if (cachedDataList.size() >= BATCH_COUNT) {
saveData();
// 存储完成清理 list
cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
}
}
/**
* 所有数据解析完成了 都会来调用
*
* @param context
*/
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
// 这里也要保存数据,确保最后遗留的数据也存储到数据库
saveData();
log.info("所有数据解析完成!");
}
/**
* 加上存储数据库
*/
private void saveData() {
log.info("{}条数据,开始存储数据库!", cachedDataList.size());
demoDAO.save(cachedDataList);
log.info("存储数据库成功!");
}
}
代码
/**
* 指定列的下标或者列名
*
* <p>1. 创建excel对应的实体对象,并使用{@link ExcelProperty}注解. 参照{@link IndexOrNameData}
* <p>2. 由于默认一行行的读取excel,所以需要创建excel一行一行的回调监听器,参照{@link IndexOrNameDataListener}
* <p>3. 直接读即可
*/
@Test
public void indexOrNameRead() {
String fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx";
// 这里默认读取第一个sheet
EasyExcel.read(fileName, IndexOrNameData.class, new IndexOrNameDataListener()).sheet().doRead();
}
导出excel示例
被导出的实体类
@Getter
@Setter
@EqualsAndHashCode
public class DemoData {
@ExcelProperty("字符串标题")
private String string;
@ExcelProperty("日期标题")
private Date date;
@ExcelProperty("数字标题")
private Double doubleData;
/**
* 忽略这个字段
*/
@ExcelIgnore
private String ignore;
}
代码
/**
* 文件下载(失败了会返回一个有部分数据的Excel)
* <p>
* 1. 创建excel对应的实体对象 参照{@link DownloadData}
* <p>
* 2. 设置返回的 参数
* <p>
* 3. 直接写,这里注意,finish的时候会自动关闭OutputStream,当然你外面再关闭流问题不大
*/
@GetMapping("download")
public void download(HttpServletResponse response) throws IOException {
// 这里注意 有同学反应使用swagger 会导致各种问题,请直接用浏览器或者用postman
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
response.setCharacterEncoding("utf-8");
// 这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系
String fileName = URLEncoder.encode("测试", "UTF-8").replaceAll("\\+", "%20");
response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");
EasyExcel.write(response.getOutputStream(), DownloadData.class).sheet("模板").doWrite(data());
}
EasyExcel实战
实战背景
双选系统,管理员在双选开始前从excel导入学生和导师信息,在双选结束后导出学生和导师的匹配信息到excel
excel导入(以导入学生为例)
被导入的实体类
/**
* excel学生导入对象
*
* @author hrh
*
*/
@Data
public class StudentExcelImport implements Serializable {
/**
* 学生学号
*/
private String studentaccount;
/**
* 学生姓名
*/
private String studentname;
/**
* 学生班级
*/
private String studentclass;
/**
* 学生学院
*/
private String studentcollege;
/**
* 性别
*/
private String gender;
private static final long serialVersionUID = 1L;
}
监听器
@Slf4j
public class StudentDataListener extends AnalysisEventListener<StudentExcelImport> {
/**
* 每隔100条存储数据库,实际使用中可以100条,然后清理list ,方便内存回收
*/
private static final int BATCH_COUNT = 100;
/**
* 缓存的数据
*/
private List<StudentExcelImport> studentExcelList = new ArrayList<>(BATCH_COUNT);
/**
* 假设这个是一个DAO,当然有业务逻辑这个也可以是一个service。当然如果不用存储这个对象没用。
*/
private StudentService studentService;
public StudentDataListener() {
// 这里是demo,所以随便new一个。实际使用如果到了spring,请使用下面的有参构造函数
// studentService = new StudentService();
}
/**
* 如果使用了spring,请使用这个构造方法。每次创建Listener的时候需要把spring管理的类传进来
*
* @param studentService
*/
public StudentDataListener(StudentService studentService) {
this.studentService = studentService;
}
/**
* 这个每一条数据解析都会来调用
*
* @param data one row value. Is is same as {@link AnalysisContext#readRowHolder()}
* @param context
*/
@Override
public void invoke(StudentExcelImport data, AnalysisContext context) {
studentExcelList.add(data);
// 达到BATCH_COUNT了,需要去存储一次数据库,防止数据几万条数据在内存,容易OOM
if (studentExcelList.size() >= BATCH_COUNT) {
saveData();
// 存储完成清理 list
studentExcelList = new ArrayList<>(BATCH_COUNT);
}
}
/**
* 所有数据解析完成了 都会来调用
*
* @param context
*/
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
// 这里也要保存数据,确保最后遗留的数据也存储到数据库
saveData();
log.info("所有数据解析完成!");
}
/**
* 加上存储数据库
*
* @return
*/
private BaseResponse saveData() {
if (CollectionUtils.isEmpty(studentExcelList)) {
return ResultUtils.error(OPERATION_ERROR, "导入失败");
}
log.info("{}条数据,开始存储数据库!", studentExcelList.size());
studentService.importStudentsFromExcel(studentExcelList);
log.info("存储数据库成功!");
return null;
}
}
Controller层代码
/**
* 管理员从excel导入学生
* @param file
* @return
*/
@PostMapping("/student/import-students")
@ApiOperation(value = "管理员从excel批量导入学生")
public BaseResponse<String> importStudents(@RequestParam("file") MultipartFile file) {
try {
EasyExcel.read(file.getInputStream(), StudentExcelImport.class, new StudentDataListener(studentService)).sheet().doRead();
return ResultUtils.success("导入成功");
} catch (Exception e) {
e.printStackTrace();
return ResultUtils.error(ErrorCode.SYSTEM_ERROR, "导入失败");
}
}
excel导出
被导出的实体类
/**
* 管理员导出匹配结果实体类
* @author hrh
*/
@Data
public class MatchRecordQueryVO implements Serializable {
/**
* 学生学号
*/
private String studentaccount;
/**
* 学生姓名
*/
private String studentname;
/**
* 学生性别
*/
private String studentsex;
/**
* 学生班级
*/
private String studentclass;
/**
* 学生学院
*/
private String studentcollege;
/**
* 导师工号
*/
private String mentoraccount;
/**
* 导师姓名
*/
private String mentorname;
/**
* 导师性别
*/
private String mentorsex;
/**
* 导师学院
*/
private String mentorcollege;
/**
* 轮次:1/2/3
*/
private Integer round;
private static final long serialVersionUID = 1L;
}
代码
/**
* 管理员导出匹配成功的导师和学生信息
* @param response
* @return
*/
@GetMapping("/match/export")
@ApiOperation(value = "管理员导出匹配成功的导师和学生信息")
public void exportMatchResult(HttpServletResponse response) throws IOException {
// 设置响应头
response.setContentType("application/vnd.ms-excel");
response.setCharacterEncoding("utf-8");
// 设置防止中文名乱码
String filename = URLEncoder.encode("双选结果", "utf-8");
// 文件下载方式(附件下载还是在当前浏览器打开)
response.setHeader("Content-disposition", "attachment;filename=" +
filename + ".xlsx");
//查询所有匹配成功的导师和学生信息
List<MatchRecordQueryVO> matchResults = matchResultService.listMatchRecordByPage(new MatchRecordQueryRequest(), 1, 1000).getRecords();
// 构建写入到excel文件的数据
EasyExcel.write(response.getOutputStream(), MatchRecordQueryVO.class)
.sheet("双选结果")
.doWrite(matchResults);
}
标签:存储,String,EasyExcel,excel,private,导入,public
From: https://blog.csdn.net/hrh1234h/article/details/144182131