当前位置:   article > 正文

springboot使用EasyExcel实现Excel导入导出_springboot excel导入

springboot excel导入

         java生成Excel比较有名的框架有Apache poijxl等,但他们都存在一个严重的问题就是非常的耗内存。如果你的系统并发量不大的话可能还行,但是一旦并发上来后一定会OOM或者JVM频繁的full gc。

        EasyExcel是阿里巴巴开源的一个excel处理框架,以使用简单、节省内存著称。EasyExcel能大大减少占用内存的主要原因是在解析Excel时没有将文件数据一次性全部载到内存中,而是从磁盘上一行行读取数据,逐个解析。EasyExcel采用一行一行的解析模式,并将一行的解析结果以观察者的模式通知处理( AnalysisEventListener )。

1、pom添加easyexcel依赖

  1. <dependency>
  2. <groupId>com.alibaba</groupId>
  3. <artifactId>easyexcel</artifactId>
  4. <version>3.1.3</version>
  5. </dependency>
  6. <dependency>
  7. <groupId>org.projectlombok</groupId>
  8. <artifactId>lombok</artifactId>
  9. <optional>true</optional>
  10. </dependency>
  11. <dependency>
  12. <groupId>cn.hutool</groupId>
  13. <artifactId>hutool-all</artifactId>
  14. <version>5.8.18</version>
  15. </dependency>

2、实体类UserDO与Excel数据对应

  1. @Data
  2. public class UserDO {
  3. // 设置excel表头名称
  4. @ExcelProperty("用户编号")
  5. @ColumnWidth(20)
  6. private Long id;
  7. // 设置该列的名称为'用户名';
  8. @ExcelProperty("用户名")
  9. // 设置表格列的宽度为20
  10. @ColumnWidth(20)
  11. private String username;
  12. // 导出时忽略该字段
  13. @ExcelIgnore
  14. private String password;
  15. @ExcelProperty("昵称")
  16. @ColumnWidth(20)
  17. private String nickname;
  18. @ExcelProperty("生日")
  19. @ColumnWidth(20)
  20. // 按照指定的格式对日期进行格式化;
  21. @DateTimeFormat("yyyy-MM-dd")
  22. private Date birthday;
  23. @ExcelProperty("手机号")
  24. @ColumnWidth(20)
  25. private String phone;
  26. @ExcelProperty("身高(米)")
  27. @NumberFormat("#.##")
  28. @ColumnWidth(20)
  29. private Double height;
  30. // 自定义内容转换器
  31. @ExcelProperty(value = "性别", converter = GenderConverter.class)
  32. @ColumnWidth(10)
  33. private Integer gender;
  34. }

常用注解有:
@ExcelProperty 指定当前字段对应excel中的哪一列。可以根据名字或者Index去匹配。当然也可以不写,默认第一个字段就是index=0,以此类推。千万注意,要么全部不写,要么全部用index,要么全部用名字去匹配。千万别三个混着用,除非你非常了解源代码中三个混着用怎么去排序的。
@ExcelIgnore EasyExcel默认所有字段都会和excel去匹配,加了这个注解会忽略该字段
@DateTimeFormat 日期转换,用String去接收excel日期格式的数据会调用这个注解。里面的value参照java.text.SimpleDateFormat
@NumberFormat 数字转换,用String去接收excel数字格式的数据会调用这个注解。里面的value参照java.text.DecimalFormat

3、converter自定义转换器

自定义转换器将数据库中表示性别的1、0转换成男、女

  3.1、GenderConverter 

  1. /**
  2. * 性别转换器
  3. * */
  4. public class GenderConverter implements Converter<Integer> {
  5. @Override
  6. public Class<?> supportJavaTypeKey() {
  7. // 实体类中对象属性类型
  8. return Integer.class;
  9. }
  10. @Override
  11. public CellDataTypeEnum supportExcelTypeKey() {
  12. // Excel中对应的CellData(单元格数据)属性类型
  13. return CellDataTypeEnum.STRING;
  14. }
  15. /**
  16. * 将单元格里的数据转为java对象,也就是女转成2,男转成1,用于导入excel时对性别字段进行转换
  17. * */
  18. @Override
  19. public Integer convertToJavaData(ReadConverterContext<?> context) throws Exception {
  20. // 从CellData中读取数据,判断Excel中的值,将其转换为预期的数值
  21. return GenderEnum.convert(context.getReadCellData().getStringValue()).getValue();
  22. }
  23. /**
  24. * 将java对象转为单元格数据,也就是2转成女,1转成男,用于导出excel时对性别字段进行转换
  25. * */
  26. @Override
  27. public WriteCellData<?> convertToExcelData(WriteConverterContext<Integer> context) throws Exception {
  28. // 判断实体类中获取的值,转换为Excel预期的值,并封装为CellData对象
  29. return new WriteCellData<>(GenderEnum.convert(context.getValue()).getDescription());
  30. }
  31. }

 3.2、GenderEnum

  1. @Getter
  2. @AllArgsConstructor
  3. public enum GenderEnum {
  4. /**
  5. * 未知
  6. */
  7. UNKNOWN(0, "未知"),
  8. /**
  9. * 男性
  10. */
  11. MALE(1, "男性"),
  12. /**
  13. * 女性
  14. */
  15. FEMALE(2, "女性");
  16. private final Integer value;
  17. @JsonFormat
  18. private final String description;
  19. public static GenderEnum convert(Integer value) {
  20. // 用于为给定元素创建顺序流
  21. // values:获取枚举类型的对象数组
  22. return Stream.of(values())
  23. .filter(bean -> bean.value.equals(value))
  24. .findAny()
  25. .orElse(UNKNOWN);
  26. }
  27. public static GenderEnum convert(String description) {
  28. return Stream.of(values())
  29. .filter(bean -> bean.description.equals(description))
  30. .findAny()
  31. .orElse(UNKNOWN);
  32. }
  33. }

4、导出

  1. /**
  2. * 设置响应结果
  3. *
  4. * @param response 响应结果对象
  5. * @param rawFileName 文件名
  6. * @throws UnsupportedEncodingException 不支持编码异常
  7. */
  8. private void setExcelResponseProp(HttpServletResponse response, String rawFileName) throws UnsupportedEncodingException {
  9. //设置内容类型
  10. response.setContentType("application/vnd.vnd.ms-excel");
  11. //设置编码格式
  12. response.setCharacterEncoding("utf-8");
  13. //设置导出文件名称(避免乱码)
  14. String fileName = URLEncoder.encode(rawFileName.concat(".xlsx"), "UTF-8");
  15. // 设置响应头
  16. response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName);
  17. }
  18. private Date getBirthday(int year, int month, int day){
  19. Calendar calendar = Calendar.getInstance();
  20. calendar.set(year, month, day);
  21. return calendar.getTime();
  22. }
  23. /**
  24. * 导出数据
  25. * */
  26. @GetMapping("/export/user")
  27. public void exportUserExcel(HttpServletResponse response) throws IOException {
  28. OutputStream outputStream = response.getOutputStream();
  29. try {
  30. this.setExcelResponseProp(response, "用户列表");
  31. // 模拟根据条件在数据库查询数据
  32. List<UserDO> userList = new ArrayList<>();
  33. for(int i = 0;i < 30;i++){
  34. UserDO userDO = new UserDO();
  35. userDO.setBirthday(getBirthday(2001,1,i));
  36. userDO.setGender(1);
  37. userDO.setHeight(Double.valueOf(i));
  38. userDO.setId(Long.valueOf(i));
  39. userDO.setPhone("138"+i);
  40. userDO.setNickname("yuanhaoz");
  41. userDO.setPassword("5849"+i);
  42. userDO.setUsername("monky"+i);
  43. userList.add(userDO);
  44. }
  45. //这个实现方式非常简单直接,使用EasyExcel的write方法将查询到的数据进行处理,以流的形式写出即可
  46. EasyExcel.write(outputStream,UserDO.class)//对应的导出实体类
  47. .excelType(ExcelTypeEnum.XLSX)//excel文件类型,包括CSV、XLS、XLSX
  48. .sheet("用户列表")//导出sheet页名称
  49. .doWrite(userList); //查询获取的数据集合List<T>,转成excel
  50. } catch (IOException e) {
  51. throw new RuntimeException(e);
  52. }finally {
  53. outputStream.flush();
  54. outputStream.close();
  55. }
  56. }

5、多sheet导出

  1. /**
  2. * 多sheet导出数据
  3. * */
  4. @GetMapping("/manySheet")
  5. public void exportManySheet(HttpServletResponse response)throws IOException{
  6. OutputStream outputStream=response.getOutputStream();
  7. ExcelWriter writer = EasyExcel.write(outputStream, UserDO.class).excelType(ExcelTypeEnum.XLSX).build();
  8. try {
  9. this.setExcelResponseProp(response, "用户列表");
  10. // 模拟根据条件在数据库分页查询数据
  11. for(int j = 1;j <= 5;j++){
  12. List<UserDO> userList = new ArrayList<>();
  13. for(int i = 0;i < 30;i++){
  14. UserDO userDO = new UserDO();
  15. userDO.setBirthday(getBirthday(2001,1,i));
  16. userDO.setGender(1);
  17. userDO.setHeight(Double.valueOf(i));
  18. userDO.setId(Long.valueOf(i));
  19. userDO.setPhone("138"+i);
  20. userDO.setNickname("yuanhaoz"+i);
  21. userDO.setPassword("5849"+i);
  22. userDO.setUsername("monky"+i);
  23. userList.add(userDO);
  24. System.out.println(i);
  25. }
  26. //创建新的sheet页
  27. WriteSheet writeSheet = EasyExcel.writerSheet("用户信息" + j).build();
  28. //将list集合中的对象写到对应的sheet中去
  29. writer.write(userList,writeSheet);
  30. }
  31. } catch (IOException e) {
  32. throw new RuntimeException(e);
  33. //给提示todo
  34. }finally {
  35. writer.finish();
  36. outputStream.flush();
  37. outputStream.close();
  38. }
  39. }

6、导入

6.1、UserListener

  1. /**
  2. * 自定义监听器,对下载的excel中的数据进行校验
  3. * */
  4. public class UserListener extends AnalysisEventListener {
  5. List<String> names = new ArrayList<>();
  6. /**
  7. * 每解析一行,回调该方法
  8. *
  9. * @param data
  10. * @param context
  11. */
  12. @Override
  13. public void invoke(Object data, AnalysisContext context) {
  14. //校验名称
  15. String name = ((UserDO) data).getUsername();
  16. if (StrUtil.isBlank(name)) {
  17. throw new RuntimeException(String.format("第%s行名称为空,请核实", context.readRowHolder().getRowIndex() + 1));
  18. }
  19. if (names.contains(name)) {
  20. throw new RuntimeException(String.format("第%s行名称已重复,请核实", context.readRowHolder().getRowIndex() + 1));
  21. } else {
  22. names.add(name);
  23. }
  24. }
  25. /**
  26. * 出现异常回调
  27. *
  28. * @param exception
  29. * @param context
  30. * @throws Exception
  31. */
  32. @Override
  33. public void onException(Exception exception, AnalysisContext context) throws Exception {
  34. if (exception instanceof ExcelDataConvertException) {
  35. /**从0开始计算*/
  36. Integer columnIndex = ((ExcelDataConvertException) exception).getColumnIndex() + 1;
  37. Integer rowIndex = ((ExcelDataConvertException) exception).getRowIndex() + 1;
  38. String message = "第" + rowIndex + "行,第" + columnIndex + "列" + "数据格式有误,请核实";
  39. throw new RuntimeException(message);
  40. } else if (exception instanceof RuntimeException) {
  41. throw exception;
  42. } else {
  43. super.onException(exception, context);
  44. }
  45. }
  46. /**
  47. * 解析完,全部回调
  48. *
  49. * @param context
  50. */
  51. @Override
  52. public void doAfterAllAnalysed(AnalysisContext context) {
  53. //解析完,全部回调逻辑实现
  54. names.clear();
  55. }
  56. }

6.2、ImportData

  1. /**
  2. * 导入数据
  3. * */
  4. @PostMapping(value = "/importData")
  5. public void ImportData(MultipartFile file){
  6. try {
  7. //获取文件的输入流
  8. InputStream inputStream = file.getInputStream();
  9. List<UserDO> list = EasyExcel.read(inputStream) //调用read方法
  10. //注册自定义监听器,字段校验可以在监听器内实现
  11. .registerReadListener(new UserListener())
  12. .head(UserDO.class) //对应导入的实体类
  13. .sheet(0) //导入数据的sheet页编号,0代表第一个sheet页,如果不填,则会导入所有sheet页的数据
  14. .headRowNumber(1) //列表头行数,1代表列表头有1行,第二行开始为数据行
  15. .doReadSync(); //开始读Excel,返回一个List<T>集合,继续后续入库操作
  16. //模拟导入数据库操作
  17. for (UserDO userDO : list){
  18. System.out.println(userDO.toString());
  19. }
  20. } catch (IOException exception){
  21. throw new RuntimeException(exception);
  22. }
  23. }

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/繁依Fanyi0/article/detail/536839
推荐阅读
相关标签
  

闽ICP备14008679号