EasyExcel简单使用
之前一直用的Apache POI来做数据的导入导出,但听说阿里的EasyExcel也拥有POI的功能的同时,在处理大数据量的导入导出的时候性能上比POI更好,所以就来尝试使用一下
导入Maven依赖:
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>3.2.1</version> <!-- 请检查并使用最新稳定版本 -->
</dependency>
导出数据功能
导出模型类
定义一个导出数据模型类,用于设置excel文件的格式,通过注解的方式可以定义excel中的格式
@ColumnWidth(20) 设置excel中列的宽度为20;
@HeadStyle(horizontalAlignment = HorizontalAlignmentEnum.CENTER) 设置文本内容是否居中;
@HeadFontStyle(bold = BooleanEnum.FALSE) 设置字体是否加粗;
@ExcelProperty(value = “电话”, index = 0) 设置了excel中的标题,value则是标题内容,index则是内容所在的列的位置
@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
@ColumnWidth(20)
@HeadStyle(horizontalAlignment = HorizontalAlignmentEnum.CENTER)
@HeadFontStyle(bold = BooleanEnum.FALSE)
public class PmembersExportVO {
@ColumnWidth(15)
@ExcelProperty(value = "电话", index = 0)
private String mobile;
@ColumnWidth(15)
@ExcelProperty(value = "姓名", index = 1)
private String realname;
@ColumnWidth(10)
@ExcelProperty(value = "性别", index = 2)
private String gender;
@ColumnWidth(10)
@ExcelProperty(value = "省份", index = 3)
private String resideprovince;
@ColumnWidth(10)
@ExcelProperty(value = "城市", index = 4)
private String residecity;
@ColumnWidth(10)
@ExcelProperty(value = "区/县", index = 5)
private String residedist;
@ColumnWidth(20)
@ExcelProperty(value = "地址", index = 6)
private String address;
@ColumnWidth(12)
@ExcelProperty(value = "公历生日", index = 7)
private String birth;
@ColumnWidth(12)
@ExcelProperty(value = "农历生日", index = 8)
private String yinlibirth;
@ColumnWidth(12)
@ExcelProperty(value = "是否闰月", index = 9)
private String isLeapMonth;
}
controller代码
@ApiOperation("导出居士信息")
@ApiImplicitParams({
@ApiImplicitParam(name = "weiqingview_backend_token", value = "token", required = true, dataType = "String", paramType = "header"),
@ApiImplicitParam(name = "uniacid", value = "Unicid", required = true, dataType = "Integer", paramType = "query")
})
@GetMapping("/export")
public void export(@RequestParam Map<String, Object> params, HttpServletResponse response) {
mcMembersService.exportExcel(params, response);
}
service代码
通过查询数据库中的数据,并封装到List集合中,调用EasyExcel的write方法即可将集合中的数据写入生成excel文件供下载
public void export(Map<String, Object> params, HttpServletResponse response) {
Integer uniacid = Integer.valueOf(params.get("uniacid").toString());
List<McMembers> mcMembersList = this.list(new QueryWrapper<McMembers>().eq("isBeliever", 1).eq("uniacid", uniacid));
List<PmembersExportVO> pmembersExportVOList = mcMembersList.stream().map(m -> {
PmembersExportVO pmembersExportVO = new PmembersExportVO();
pmembersExportVO.setAddress(m.getAddress());
pmembersExportVO.setMobile(m.getMobile());
pmembersExportVO.setGender(ObjectUtils.isEmpty(m.getGender()) ? "" : m.getGender().equals(0) ? "女" : "男");
pmembersExportVO.setBirth(m.getBirth());
pmembersExportVO.setYinlibirth(m.getYinlibirth());
pmembersExportVO.setRealname(m.getRealname());
pmembersExportVO.setResidedist(m.getResidedist());
pmembersExportVO.setResidecity(m.getResidecity());
pmembersExportVO.setResideprovince(m.getResideprovince());
pmembersExportVO.setIsLeapMonth(ObjectUtils.isEmpty(m.getIsLeapMonth()) ? "否" : m.getIsLeapMonth() == 1 ? "是" : "否");
return pmembersExportVO;
}).collect(Collectors.toList());
// // 导出Excel
// EasyExcel.write("居士信息数据.xls", PmembersExportVO.class)
.head(headersList) // 设置表头
// .sheet("用户信息")
// .doWrite(pmembersExportVOList);
// 设置响应头
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
response.setCharacterEncoding("utf-8");
String fileName = null;
try {
fileName = URLEncoder.encode("居士信息数据.xlsx", "UTF-8");
response.setHeader("Content-Disposition", "attachment;filename=" + fileName);
EasyExcel.write(response.getOutputStream(), PmembersExportVO.class)
.sheet("用户信息")
.doWrite(pmembersExportVOList);
} catch (UnsupportedEncodingException e) {
log.error("导出居士信息报错 UnsupportedEncodingException:{}", e);
} catch (IOException e) {
log.error("导出居士信息报错 IOException:{}", e);
}
}
导入数据功能:
导入数据模型类
我这里用的跟导出数据类基本一致,所以也就改了个类名
只要用@ExcelProperty指定对应列的信息就行,其他指定格式的注解其实可以忽略
@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
@ColumnWidth(20)
@HeadStyle(horizontalAlignment = HorizontalAlignmentEnum.CENTER)
@HeadFontStyle(bold = BooleanEnum.FALSE)
public class ImportPmcMembersDTO {
@ColumnWidth(15)
@ExcelProperty(value = "电话", index = 0)
private String mobile;
@ColumnWidth(15)
@ExcelProperty(value = "姓名", index = 1)
private String realname;
@ColumnWidth(10)
@ExcelProperty(value = "性别", index = 2)
private String gender;
@ColumnWidth(10)
@ExcelProperty(value = "省份", index = 3)
private String resideprovince;
@ColumnWidth(10)
@ExcelProperty(value = "城市", index = 4)
private String residecity;
@ColumnWidth(10)
@ExcelProperty(value = "区/县", index = 5)
private String residedist;
@ColumnWidth(20)
@ExcelProperty(value = "地址", index = 6)
private String address;
@ColumnWidth(12)
@ExcelProperty(value = "公历生日", index = 7)
private String birth;
@ColumnWidth(12)
@ExcelProperty(value = "农历生日", index = 8)
private String yinlibirth;
@ColumnWidth(12)
@ExcelProperty(value = "是否闰月", index = 9)
private String isLeapMonth;
}
controller代码
前端页面上传一个name是file的文件
@ApiOperation("导入居士信息")
@ApiImplicitParams({
@ApiImplicitParam(name = "weiqingview_backend_token", value = "token", required = true, dataType = "String", paramType = "header"),
@ApiImplicitParam(name = "uniacid", value = "Unicid", required = true, dataType = "Integer", paramType = "query"),
@ApiImplicitParam(name = "file", value = "file", required = true, dataType = "MultipartFile", paramType = "query")
})
@PostMapping("/uploadExcel")
public R uploadExcel(@RequestParam("file") MultipartFile file, @RequestParam Map<String, Object> params) {
return mcMembersService.importExcel(file, params);
}
service代码
下面是接收controller传来的文件后,通过调用EasyExcel的read方法,直接进行导入操作
public R importExcel(MultipartFile file, Map<String, Object> params) {
// 检查文件是否为空
if (file.isEmpty()) {
return R.error("没有检测到文件"); // 返回错误页面
}
try {
// 执行导入操作
EasyExcel.read(file.getInputStream(), ImportPmcMembersDTO.class, new ImportPmcMembersListener(this, params)).sheet().doRead();
// 返回上传成功页面
return R.ok("导入excel成功");
} catch (IOException e) {
e.printStackTrace();
return R.error("导入excel失败");
}
}
下面是service中基本的添加数据的操作方法,导入数据插库的时候也直接调用,就不写新的插库方法了
public R pAdd(AddPmcMembersDTO addPmcMembersDTO, Map<String, Object> params) {
McMembers mcMembers = new McMembers();
BeanUtils.copyProperties(addPmcMembersDTO, mcMembers);
mcMembers.setIsBeliever((byte) 1);
Integer uniacid = Integer.parseInt(params.get("uniacid").toString());
mcMembers.setUniacid(uniacid);
if (this.save(mcMembers)) {
return R.ok("添加成功");
}
return R.error("添加失败");
}
listener代码
上传的话需要定义一个listener,并继承AnalysisEventListener,这里的T类型就是上面定义的模型类的类型。
如果是在listener中进行插库操作,那需要把service注入进来,但是在listener中不能用@Autowired,所以重写一个带参的构造方法,把注入好的service直接传进来使用即可,我下面将注入好的mcMembersService传递了进来,执行插库操作。下面的AddPmcMembersDTO是我另外定义的一个模型类,是用于界面上添加数据用的,这里直接转换后调用进行插库操作了。
当然也可以在listener中先将excel中的数据都封装到List集合中,再统一将List中的数据插库也行。
public class ImportPmcMembersListener extends AnalysisEventListener<ImportPmcMembersDTO> {
private McMembersService mcMembersService;
private Map<String, Object> params;
//通过构造方法,得到注入好的mcMembersService
public ImportPmcMembersListener(McMembersService mcMembersService, Map<String, Object> params) {
this.mcMembersService = mcMembersService;
this.params = params;
}
@Override
public void invoke(ImportPmcMembersDTO data, AnalysisContext context) {
// 处理读取到的每行数据,例如保存到数据库
System.out.println("读取到一行数据: " + data.toString());
// 这里可以添加保存到数据库的逻辑
AddPmcMembersDTO addPmcMembersDTO = new AddPmcMembersDTO();
addPmcMembersDTO.setMobile(data.getMobile());
addPmcMembersDTO.setRealname(data.getRealname());
addPmcMembersDTO.setGender((byte) (ObjectUtils.isEmpty(data.getGender()) ? 0 : data.getGender().equals("男") ? 1 : 2));
addPmcMembersDTO.setAddress(data.getAddress());
addPmcMembersDTO.setBirth(data.getBirth());
addPmcMembersDTO.setYinlibirth(data.getYinlibirth());
addPmcMembersDTO.setResidedist(data.getResidedist());
addPmcMembersDTO.setResidecity(data.getResidecity());
addPmcMembersDTO.setResideprovince(data.getResideprovince());
addPmcMembersDTO.setIsLeapMonth((byte) (ObjectUtils.isEmpty(data.getIsLeapMonth()) ? 0 : data.getIsLeapMonth().equals("是") ? 1 : 0));
// 进行插库操作
mcMembersService.pAdd(addPmcMembersDTO, params);
}
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
// 所有数据解析完毕后的回调
System.out.println("所有数据解析完成");
}
将一下excel数据进行导入操作
用Api工具测试了下,上传成功
查看数据库,数据也成功导入
整体感觉挺好用的,对于不同的数据进行导入的话,其实可以再封装成通用的listener,这样不用每一个功能导入都去定义一个listener,这个等后面有空再折腾吧。