手把手教你利用easyexcel导出数据【附带源码】

打印 上一主题 下一主题

主题 859|帖子 859|积分 2577

一、前言

​        项目开发过程中,免不了必要数据导出功能,常见的导出工具包有poi,easypoi,easyexcel,它们各有优缺点,简要来说:

  • poi:功能强大,利用起来相对复杂,大数据时大概导致内存溢出
  • easypoi:基于poi实现,功能强大,简单易用,大数据时大概导致内存溢出,小数据量时推荐
  • easyexcel:基于poi实现,性能更优,大数据量时推荐
  • 本文只介绍easyexcel,并介绍常见题目比如字典、样式的办理方案
二、业务流程

​        介绍数据导出业务流程之前,先梳理下数据导出,什么是数据导出,为什么必要数据导出?
​        我们都知道数据存储在数据库中,数据库中的数据可以通过前端页面或者APP出现给利用者,其实通过excel将数据导出也是一种出现方式,而且可以通过excel对数据做更复杂的处理惩罚。
​        前面说了,excel导出和前端页面、APP一样都是一种出现方式,所以前端页面和APP在将数据出现给用户的过程中遇到的题目,像属性转换(数据库存储的name,要出现给利用者名称)、字典转换(数据库存储的1/0,要出现给利用者启用/停用)等等题目,excel导出一样会遇到。下面介绍下数据出现必须要经过业务流程

  • 获取必要导出的数据集合
  • 数据属性和自然语言映射关系,将数据对象的属性转为利用者可以理解的自然语言
  • 数据字典值和自然语言映射关系,将属性的字典值转为利用者可以理解的自然语言(非字典值,是什么值就出现什么值)
  • 数据样式和自然语言样式映射关系,将数据样式转为利用者可以理解的自然语言样式
  • 设置表格样式
  • 将数据集合按照上述映射关系和表格样式,写入到excel中
  • 用户下载excel
三、实现

1、引入easyexcel、fastjson、lombok包
  1. <dependency>
  2.     <groupId>com.alibaba</groupId>
  3.     <artifactId>easyexcel</artifactId>
  4.     <version>4.0.3</version>
  5. </dependency>
  6. <dependency>
  7.     <groupId>com.alibaba</groupId>
  8.     <artifactId>fastjson</artifactId>
  9.     <version>1.2.46</version>
  10. </dependency>
  11. <dependency>
  12.     <groupId>org.projectlombok</groupId>
  13.     <artifactId>lombok</artifactId>
  14.     <version>1.18.0</version>
  15. </dependency>
复制代码
2、创建Json工具类
  1. package com.yu.demo.tools;
  2. import com.alibaba.fastjson.JSON;
  3. import com.fasterxml.jackson.core.type.TypeReference;
  4. import java.lang.reflect.Type;
  5. import java.util.Map;
  6. /**
  7. * JSON工具类
  8. *
  9. * @author admin
  10. */
  11. public abstract class JsonUtil {
  12.     private JsonUtil() {
  13.     }
  14.     public final static Type MAP_INTEGER_STRING = new TypeReference<Map<Integer, String>>() {
  15.     }.getType();
  16.     /**
  17.      * json串转Map(Map的value类型一致时使用)
  18.      *
  19.      * @param jsonString json串
  20.      * @return 对象
  21.      */
  22.     public static <K, V> Map<K, V> json2Map(String jsonString, Type type) {
  23.         return JSON.parseObject(jsonString, type);
  24.     }
  25. }
复制代码
3、创建自定义字典转换注解
  1. package com.yu.demo.tools;
  2. import java.lang.annotation.*;
  3. @Target({ElementType.FIELD})
  4. @Retention(RetentionPolicy.RUNTIME)
  5. @Documented
  6. public @interface DictSource {
  7.     /**
  8.      * 字典类型主键
  9.      */
  10.     String dictTypeId() default "";
  11.     /**
  12.      * 字典内容json串
  13.      */
  14.     String dictContentJson() default "";
  15. }
复制代码
4、创建字典转换实现类
  1. package com.yu.demo.tools;
  2. import com.alibaba.excel.converters.Converter;
  3. import com.alibaba.excel.enums.CellDataTypeEnum;
  4. import com.alibaba.excel.metadata.GlobalConfiguration;
  5. import com.alibaba.excel.metadata.data.WriteCellData;
  6. import com.alibaba.excel.metadata.property.ExcelContentProperty;
  7. import org.apache.poi.util.StringUtil;
  8. import java.lang.reflect.Field;
  9. import java.util.Map;
  10. public class IntegerDictConverter implements Converter<Integer> {
  11.     @Override
  12.     public CellDataTypeEnum supportExcelTypeKey() {
  13.         return CellDataTypeEnum.STRING;
  14.     }
  15.     @Override
  16.     public WriteCellData<?> convertToExcelData(Integer value, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) {
  17.         //属性值为空时,直接返回
  18.         if (value == null) {
  19.             //为空时的处理,与前端展示保持一致即可
  20.             return new WriteCellData<>("");
  21.         }
  22.         //获取添加@ExcelProperty注解且converter = IntegerDictConverter.class的属性
  23.         Field field = contentProperty.getField();
  24.         //获取该属性的DictConverter注解信息
  25.         DictSource dictSource = field.getAnnotation(DictSource.class);
  26.         //配置了converter = IntegerDictConverter.class的属性,但是没有添加DictSource注解的直接返回
  27.         if (dictSource == null) {
  28.             return new WriteCellData<>(String.valueOf(value));
  29.         }
  30.         //获取配置的dictTypeId
  31.         String dictTypeId = dictSource.dictTypeId();
  32.         //获取配置的dictContentJson
  33.         String dictContentJson = dictSource.dictContentJson();
  34.         //判断dictTypeId是否为空
  35.         boolean nullDictType = StringUtil.isBlank(dictTypeId);
  36.         //判断nullDictContentJson是否为空
  37.         boolean nullDictContentJson = StringUtil.isBlank(dictContentJson);
  38.         //字典配置都为空时,将属性值转为字符串直接返回
  39.         if (nullDictType && nullDictContentJson) {
  40.             return new WriteCellData<>(String.valueOf(value));
  41.         }
  42.         //优先使用dictTypeId处理转换
  43.         if (!nullDictType) {
  44.             //通过dictTypeId获取字典内容集合:List<DictContent> dictContents = dictContentService.listByDictTypeId(dictTypeId);//主键是数值的,将dictTypeId转为数值
  45.             //遍历字典内容,匹配属性值与字典值:value.equals(dictContent.getValue())
  46.             //匹配成功后获取字典名称返回:return new WriteCellData<>(dictContent.getName());
  47.             //如果没有匹配成功使用dictContentJson处理转换
  48.         }
  49.         if (!nullDictContentJson) {
  50.             Map<Integer, String> dictContentMap = JsonUtil.json2Map(dictContentJson, JsonUtil.MAP_INTEGER_STRING);
  51.             String cnName = dictContentMap.get(value);
  52.             if (StringUtil.isNotBlank(cnName)) {
  53.                 return new WriteCellData<>(cnName);
  54.             }
  55.         }
  56.         //没有转换成功时使用默认属性值
  57.         return new WriteCellData<>(String.valueOf(value));
  58.     }
  59. }
复制代码
5、创建数据对象类
  1. package com.yu.demo.web.easyexcel.entity;
  2. import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
  3. import com.alibaba.excel.annotation.ExcelProperty;
  4. import com.alibaba.excel.annotation.format.DateTimeFormat;
  5. import com.alibaba.excel.annotation.write.style.ColumnWidth;
  6. import com.alibaba.excel.converters.date.DateStringConverter;
  7. import com.yu.demo.web.easyexcel.component.DictSource;
  8. import com.yu.demo.web.easyexcel.component.IntegerDictConverter;
  9. import lombok.Getter;
  10. import lombok.Setter;
  11. import lombok.ToString;
  12. import java.util.Date;
  13. @Setter
  14. @Getter
  15. @ToString
  16. //类上添加@ExcelIgnoreUnannotated时,属性没有@ExcelProperty注解时不导出
  17. //类上未添加@ExcelIgnoreUnannotated,属性没有@ExcelProperty注解时也导出
  18. @ExcelIgnoreUnannotated
  19. public class User {
  20.     /**
  21.      * 名称
  22.      */
  23.     @ExcelProperty("名称")
  24.     private String name;
  25.     /**
  26.      * 密码
  27.      * 类添加@ExcelIgnoreUnannotated,属性未添加@ExcelProperty,不导出
  28.      */
  29.     private String password;
  30.     /**
  31.      * 生日
  32.      * 日期样式处理
  33.      * 1.使用@DateTimeFormat设置导出样式
  34.      * 2.使用DateStringConverter处理导出
  35.      */
  36.     @DateTimeFormat("yyyy-MM-dd HH:mm:ss")
  37.     @ExcelProperty(value = "生日", converter = DateStringConverter.class)
  38.     private Date birthday;
  39.     /**
  40.      * 性别
  41.      * 字典转换处理
  42.      */
  43.     @ColumnWidth(7)//指定列宽度,优先级高于LongestMatchColumnWidthStyleStrategy
  44.     @ExcelProperty(value = "性别", converter = IntegerDictConverter.class)
  45.     @DictSource(dictContentJson = "{0:'女',1:'男',2:'保密'}")
  46.     private Integer sex;
  47. }
复制代码
6、创建多sheet页封装对象
  1. package com.yu.demo.tools;
  2. import lombok.Getter;
  3. import lombok.Setter;
  4. import lombok.ToString;
  5. import java.util.List;
  6. /**
  7. * excel导入导出数据对象
  8. */
  9. @Setter
  10. @Getter
  11. @ToString
  12. public class SheetEntity<T> {
  13.     /**
  14.      * sheet页名称(导出参数)
  15.      * 可以为空,为空时,单sheet页没有名称,多sheet页序号为名称
  16.      */
  17.     private String sheetName;
  18.     /**
  19.      * 数据类型(导入导出参数)
  20.      */
  21.     private Class<T> head;
  22.     /**
  23.      * 数据(导出参数)
  24.      */
  25.     private List<T> data;
  26. }
复制代码
7、创建Excel导出工具类

​        导出的数据有如下三种及其说明

  • 通过全路径文件名导出,easyexcel通过全路径文件名创建文件,将数据写入文件,当路径不存在时报错,适合场景:一次导出,多次下载
  • 通过文件导出,将数据写入文件,当路径不存在报错,适合场景:一次导出,多次下载
  • 通过输出流导出,将数据写入输出流,适合场景:导出一次下载一次
  1. package com.yu.demo.tools;
  2. import com.alibaba.excel.EasyExcel;
  3. import com.alibaba.excel.ExcelWriter;
  4. import com.alibaba.excel.support.ExcelTypeEnum;
  5. import com.alibaba.excel.write.builder.ExcelWriterBuilder;
  6. import com.alibaba.excel.write.handler.WriteHandler;
  7. import com.alibaba.excel.write.metadata.WriteSheet;
  8. import com.alibaba.excel.write.metadata.style.WriteCellStyle;
  9. import com.alibaba.excel.write.style.HorizontalCellStyleStrategy;
  10. import com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy;
  11. import org.apache.poi.ss.usermodel.HorizontalAlignment;
  12. import org.apache.poi.ss.usermodel.VerticalAlignment;
  13. import org.apache.poi.util.StringUtil;
  14. import java.io.File;
  15. import java.io.OutputStream;
  16. import java.util.List;
  17. /**
  18. * excel导入导出工具类(easyExcel实现)
  19. * easyPoi:并发量和数据量都不大时推荐,定制化的导出支持非常的丰富
  20. * easyExcel:高并发、大数据量时推荐
  21. */
  22. public abstract class ExcelUtil {
  23.     // 设置居中对齐的样式
  24.     private static final WriteCellStyle CONTENT_WRITE_CELL_STYLE;
  25.     private static final WriteHandler HORIZONTAL_CELL_STYLE_STRATEGY;
  26.     static {
  27.         CONTENT_WRITE_CELL_STYLE = new WriteCellStyle();
  28.         //水平居中
  29.         CONTENT_WRITE_CELL_STYLE.setHorizontalAlignment(HorizontalAlignment.CENTER);
  30.         //垂直居中
  31.         CONTENT_WRITE_CELL_STYLE.setVerticalAlignment(VerticalAlignment.CENTER);
  32.         HORIZONTAL_CELL_STYLE_STRATEGY = new HorizontalCellStyleStrategy(null, CONTENT_WRITE_CELL_STYLE);
  33.     }
  34.     private ExcelUtil() {
  35.     }
  36.     /**
  37.      * 使用EasyExcel导出
  38.      *
  39.      * @param fullFileName 文件路径+文件名+后缀(文件已存在时覆盖)
  40.      * @param sheetName    sheet名称(为空时使用默认值0)
  41.      * @param head         数据类型(为空时没有表头,只有数据)
  42.      * @param exportData   需要导出的数据(为空时,没有数据)
  43.      */
  44.     public static void exportByEasyExcel(String fullFileName, String sheetName, Class<?> head, List<?> exportData) {
  45.         File targetFile = new File(fullFileName);
  46.         // 判断文件父目录是否存在
  47.         if (!targetFile.getParentFile().exists()) {
  48.             boolean mkdirResult = targetFile.getParentFile().mkdirs();
  49.             if (!mkdirResult) {
  50.                 return;
  51.             }
  52.         }
  53.         ExcelWriterBuilder excelWriterBuilder = EasyExcel.write(targetFile, head);
  54.         if (fullFileName.endsWith(ExcelTypeEnum.XLS.getValue())) {
  55.             excelWriterBuilder.excelType(ExcelTypeEnum.XLS);
  56.         } else if (fullFileName.endsWith(ExcelTypeEnum.CSV.getValue())) {
  57.             excelWriterBuilder.excelType(ExcelTypeEnum.CSV);
  58.         } else {
  59.             excelWriterBuilder.excelType(ExcelTypeEnum.XLSX);
  60.         }
  61.         excelWriterBuilder
  62.                 //设置列按最大长度调整
  63.                 .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
  64.                 //设置水平垂直居中
  65.                 .registerWriteHandler(HORIZONTAL_CELL_STYLE_STRATEGY)
  66.                 .sheet(sheetName)
  67.                 .doWrite(exportData);
  68.     }
  69.     /**
  70.      * 使用EasyExcel导出
  71.      *
  72.      * @param outputStream 输出流
  73.      * @param sheetName    sheet名称(为空时使用默认值0)
  74.      * @param head         数据类型(为空时没有表头,只有数据)
  75.      * @param exportData   需要导出的数据(为空时,没有数据)
  76.      */
  77.     public static void exportByEasyExcel(OutputStream outputStream, ExcelTypeEnum excelType, String sheetName, Class<?> head, List<?> exportData) {
  78.         EasyExcel.write(outputStream, head)
  79.                 .excelType(excelType)
  80.                 //设置列按最大长度调整,非线程安全,每次都需要new
  81.                 .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
  82.                 //设置水平垂直居中
  83.                 .registerWriteHandler(HORIZONTAL_CELL_STYLE_STRATEGY)
  84.                 .sheet(sheetName)
  85.                 .doWrite(exportData);
  86.     }
  87.     /**
  88.      * 使用EasyExcel导出多sheet页数据
  89.      *
  90.      * @param outputStream  输出流
  91.      * @param sheetEntities 导出数据对象集合
  92.      */
  93.     public static void exportByEasyExcel(OutputStream outputStream, ExcelTypeEnum excelType, List<SheetEntity<?>> sheetEntities) {
  94.         ExcelWriterBuilder excelWriterBuilder = EasyExcel.write(outputStream).excelType(excelType);
  95.         writeSheets(excelWriterBuilder, sheetEntities);
  96.     }
  97.     private static void writeSheets(ExcelWriterBuilder excelWriterBuilder, List<SheetEntity<?>> sheetEntities) {
  98.         excelWriterBuilder
  99.                 .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
  100.                 .registerWriteHandler(HORIZONTAL_CELL_STYLE_STRATEGY);
  101.         ExcelWriter excelWriter = excelWriterBuilder.build();
  102.         for (int i = 0; i < sheetEntities.size(); i++) {
  103.             SheetEntity<?> sheetEntity = sheetEntities.get(i);
  104.             Class<?> head = sheetEntity.getHead();
  105.             List<?> exportData = sheetEntity.getData();
  106.             String sheetName = StringUtil.isBlank(sheetEntity.getSheetName()) ? String.valueOf(i + 1) : sheetEntity.getSheetName();
  107.             WriteSheet writeSheet = EasyExcel.writerSheet(i + 1, sheetName).head(head).build();
  108.             excelWriter.write(exportData, writeSheet);
  109.         }
  110.         excelWriter.finish();
  111.     }
  112. }
复制代码
8、创建测试类
  1. package com.yu.demo.web.easyexcel.web;
  2. import com.alibaba.excel.support.ExcelTypeEnum;
  3. import com.yu.demo.web.easyexcel.entity.SheetEntity;
  4. import com.yu.demo.web.easyexcel.entity.User;
  5. import com.yu.demo.web.easyexcel.util.ExcelUtil;
  6. import org.springframework.beans.factory.annotation.Value;
  7. import org.springframework.http.HttpHeaders;
  8. import org.springframework.web.bind.annotation.GetMapping;
  9. import org.springframework.web.bind.annotation.RequestMapping;
  10. import org.springframework.web.bind.annotation.RestController;
  11. import javax.annotation.PostConstruct;
  12. import javax.servlet.http.HttpServletResponse;
  13. import java.io.IOException;
  14. import java.net.URLEncoder;
  15. import java.nio.charset.StandardCharsets;
  16. import java.util.ArrayList;
  17. import java.util.Date;
  18. import java.util.List;
  19. @RestController
  20. @RequestMapping("user")
  21. public class UserController {
  22.     @Value("${download.path}")
  23.     private String filePath;
  24.     private List<User> users;
  25.     private List<SheetEntity<?>> sheetEntities;
  26.     @PostConstruct
  27.     public void init() {
  28.         users = new ArrayList<>(5);
  29.         for (int i = 0; i < 5; i++) {
  30.             User user = new User();
  31.             user.setName(i + "号用户");
  32.             user.setPassword(String.valueOf(i * 1000));
  33.             user.setBirthday(new Date());
  34.             user.setSex(i % 3);
  35.             users.add(user);
  36.         }
  37.         sheetEntities = new ArrayList<>(2);
  38.         for (int i = 0; i < 2; i++) {
  39.             SheetEntity<User> sheetEntity = new SheetEntity<>();
  40.             sheetEntity.setSheetName(i + "号sheet");
  41.             sheetEntity.setHead(User.class);
  42.             sheetEntity.setData(users);
  43.             sheetEntities.add(sheetEntity);
  44.         }
  45.     }
  46.     /**
  47.      * 单sheet页通过全路径文件名导出测试接口(也可以通过文件流导出)
  48.      * 返回文件名,前端通过web路径+文件名下载文件
  49.      */
  50.     @GetMapping("/filePath")
  51.     public String filePath() {
  52.         String fileName = "用户.xlsx";
  53.         String fullFileName = filePath + fileName;
  54.         ExcelUtil.exportByEasyExcel(fullFileName, "用户", User.class, users);
  55.         return fileName;
  56.     }
  57.     /**
  58.      * 多sheet页通过文件流导出(也可以通过全路径文件名导出)
  59.      */
  60.     @GetMapping("/download")
  61.     public void download(HttpServletResponse response) throws IOException {
  62.         String fileName = "用户";
  63.         response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
  64.         response.setCharacterEncoding(StandardCharsets.UTF_8.name());
  65.         String encodeFileName = URLEncoder.encode(fileName, "UTF-8").replaceAll("\\+", "%20");
  66.         response.setHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=*=utf-8''" + encodeFileName + ExcelTypeEnum.XLSX.getValue());
  67.         ExcelUtil.exportByEasyExcel(response.getOutputStream(), ExcelTypeEnum.XLSX, sheetEntities);
  68.     }
  69. }
复制代码
9、application.yml中添加文件下载路径配置
  1. download:
  2.   #配置文件下载路径
  3.   path: C:\Users\Administrator\Desktop
  4. spring:
  5.   web:
  6.     resources:
  7.       static-locations:
  8.         #注册文件下载路径
  9.         - file:${download.path}
  10.         #系统默认配置
  11.         - classpath:/META-INF/resources/
  12.         - classpath:/resources/
  13.         - classpath:/static/
  14.         - classpath:/public/
复制代码
四、接口测试

1、启动项目


2、利用全路径文件名方式导出



3、利用文件流方式导出


五、总结


  • 利用Entity对象作为关系映射的载体,利用@ExcelProperty注解映射属性名称,并可以指定转换器、序号等信息;利用@DateTimeFormat注解和指定转换器设置时间格式;利用自定义注解@DictSource注解和指定转换器转换字典值
  • 利用@ColumnWidth或其同目录下的其他注解、WriteHandler设置Excel样式
  • 利用全路径文件名、文件、文件流作为数据导出的载体导出数据。
  • SpringBoot集成easyexcel数据导出案例下载

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

耶耶耶耶耶

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表