5、基于EasyExcel的导入导出
一、Apach POI处理Excel的方式:
传统Excel操作或者解析都是利用Apach POI进行操作,POI中处理Excel有以下几种方式:
1、HSSFWorkbook:
HSSFWorkbook用来处理.xls后缀的Excel,即适用于Excel2003以前(包括2003)的版本。因为其最大只能处理65535行的数据,所以现在已经很少使用了
2、XSSFWorkbook:
XSSFWorkbook是现在处理Excel比较常见的方式。其适用于.xlsx后缀的Excel,即Excel2007后的版本。能够最多处理104万行数据。但是其在读取/处理Excel时会一口气将Excel内容写入到内存,因此在处理的Excel文件较大时可能打爆内存,造成OOM异常(内存溢出)。
3、SXSSFWorkbook:
SXSSFWorkbook相当于是XSSFWorkbook的改良版本,在初始化SXSSFWorkbook实例时,需要填写一个缓冲行数参数(默认100行),当读入到内存中的数据超过该数值后,会像队列一样将最前面的数据保存到硬盘中,从而避免出现OOM。这么一看该方式简直完美啊,不过因为超过缓存行的数据都写到硬盘中了,所以如果你想要获取这块的内容(比如复制这块内容到另一个Excel中)就会发现取不到了,因为不在内存中,所以无法通过SXSSFWorkbook实例获取该部分内容。
二、Apach POI框架的不足:
1、使用步骤繁琐;
2、动态写出Excel操作非常麻烦;
3、对于新手来说,很难在短时间内上手;
4、读写时需要占用较大的内容,当数据量大时容器发生OOM;
基于上述原因,阿里开源出一款易上手,且比较节省内存的Excel操作框架:EasyExcel
三、Apach POI、EasyPoi与EasyExcel的区别:
1、POI 优点在于自由,但是迎来的就是复杂度,和大数据量时候性能的缺点
2、EasyPoi基于POI 的二次封装,解决了大部分的常用场景,简化了代码,但是特别复杂表格处理还是不行,而且性能的话和poi差不多,简单来说就是简化了Poi的操作,少些点代码,总体来说,easypoi和easyexcel都是基于apache poi进行二次开发的。
3、easypoi和easyexcel的不同点在于:
(1)、easypoi 在读写数据的时候,优先是先将数据写入内存,优点是读写性能非常高,但是当数据量很大的时候,会出现oom,当然它也提供了 sax 模式的读写方式,需要调用特定的方法实现。
(2)、easyexcel 基于sax模式进行读写数据,不会出现oom情况,程序有过高并发场景的验证,因此程序运行比较稳定,相对于 easypoi 来说,读写性能稍慢!
(3)、easypoi 与 easyexcel 还有一点区别在于,easypoi 对定制化的导出支持非常的丰富,如果当前的项目需求,并发量不大、数据量也不大,但是需要导出 excel 的文件样式千差万别,那么我推荐你用 easypoi;反之,使用 easyexcel !
四、EasyExcel的使用:
4.1、操作流程:
建工程——》改POM——》写YML——》业务类
4.1.1、添加依赖:
<!-- easyexcel 依赖 --> <dependency> <groupId>com.alibaba</groupId> <artifactId>easyexcel</artifactId> <version>2.2.7</version> </dependency>
4.1.2、写YML:
server: port: 8081 spring: application: name: demo #项目名 datasource: type: com.alibaba.druid.pool.DruidDataSource #当前数据源操作类型 driver-class-name: org.gjt.mm.mysql.Driver #mysql驱动包 url: jdbc:mysql://localhost:3306/demo?useUnicode=true&characterEncoding=utf-8&useSSL=false username: root password: 123456 druid: test-while-idle: false #关闭空闲检测 mybatis: mapperLocations: classpath:mapper/*.xml #resource目录下建mapper包,存放xml文件
4.2、EasyExcel的导出操作(单个与批量导出):
4.2.1、通用导出工具类ExportUtil:
import com.alibaba.excel.EasyExcel; import com.alibaba.excel.support.ExcelTypeEnum; import com.alibaba.excel.write.metadata.style.WriteCellStyle; import com.alibaba.excel.write.metadata.style.WriteFont; import com.alibaba.excel.write.style.HorizontalCellStyleStrategy; import com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy; import lombok.extern.slf4j.Slf4j; import org.apache.poi.ss.usermodel.BorderStyle; import org.apache.poi.ss.usermodel.HorizontalAlignment; import org.apache.poi.ss.usermodel.IndexedColors; import javax.servlet.http.HttpServletResponse; import java.io.OutputStream; import java.util.List; @Slf4j public class ExportUtil { /** * 导出 Excel * * @param response :响应 * @param data :导出的list集合 * @param fileName :Excel名(最好英文,无需后缀) * @param sheetName :sheet页名 * @param clazz :导出Excel实体类 * @throws Exception */ public static void writeExcel(HttpServletResponse response, List<? extends Object> data, String fileName, String sheetName, Class clazz) throws Exception { //表头样式 WriteCellStyle headWriteCellStyle = new WriteCellStyle(); //设置表头居中对齐 headWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER); //内容样式 WriteCellStyle contentWriteCellStyle = new WriteCellStyle(); //设置内容靠左对齐 contentWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.LEFT); HorizontalCellStyleStrategy horizontalCellStyleStrategy = new HorizontalCellStyleStrategy(headWriteCellStyle, contentWriteCellStyle); EasyExcel.write(getOutputStream(fileName, response), clazz) .excelType(ExcelTypeEnum.XLSX)//读取的文件类型 .sheet(sheetName)//读取的sheet,可以是行号也可以是sheet名 .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())//设置自动适应宽度 .registerWriteHandler(horizontalCellStyleStrategy)//设置样式(或:registerWriteHandler(createStyleStrategy)) .doWrite(data); } /** * 格式处理 */ private static OutputStream getOutputStream(String fileName, HttpServletResponse response) throws Exception { response.setContentType("application/vnd.ms-excel;charset=utf-8"); response.setCharacterEncoding("utf8"); response.addHeader("Content-disposition", "attachment;filename=" + fileName + ExcelTypeEnum.XLSX.getValue()); return response.getOutputStream(); } /** * 自定义样式 */ private static HorizontalCellStyleStrategy createStyleStrategy() { // 头的策略 WriteCellStyle headWriteCellStyle = new WriteCellStyle(); // 背景设置为红色 headWriteCellStyle.setFillForegroundColor(IndexedColors.LIGHT_CORNFLOWER_BLUE.getIndex()); WriteFont headWriteFont = new WriteFont(); headWriteFont.setFontHeightInPoints((short) 10); headWriteCellStyle.setWriteFont(headWriteFont); // 内容的策略 WriteCellStyle contentWriteCellStyle = new WriteCellStyle(); contentWriteCellStyle.setBorderBottom(BorderStyle.THIN); //底边框 contentWriteCellStyle.setBorderLeft(BorderStyle.THIN); //左边框 contentWriteCellStyle.setBorderRight(BorderStyle.THIN); //右边框 contentWriteCellStyle.setBorderTop(BorderStyle.THIN); //顶边框 WriteFont contentWriteFont = new WriteFont(); // 字体大小 contentWriteFont.setFontHeightInPoints((short) 10); contentWriteCellStyle.setWriteFont(contentWriteFont); // 执行策略 HorizontalCellStyleStrategy horizontalCellStyleStrategy = new HorizontalCellStyleStrategy(headWriteCellStyle, contentWriteCellStyle); return horizontalCellStyleStrategy; } }
4.2.2、业务流程:
1、请求体:
import lombok.Data; import java.util.List; @Data public class RequestVO { private String id; private List<String> ids; private Boolean way; }
2、控制类:
/** * 导出 * 1、单个导出 * 2、批量导出 * * http://localhost:8081/v1/export-data * { * "id":"xxx", * "ids":["xxx","xxx"], * "way":false * } * */ @PostMapping("/export-data") public void exportList( @RequestBody RequestVO requestVO, HttpServletResponse response) { //导出方式:单个/批量 if(true == requestVO.getWay()){ requestVO.setId(null); } operateService.exportData(requestVO,response); }
3、业务类:
@Autowired private OperateMapper operateMapper; /** * 导出操作 */ @Override public void exportData(RequestVO requestVO, HttpServletResponse response) { //mysql查询 List<BasicDemo> list = operateMapper.selectAllDemo(requestVO); //转换 List<ExportExcelVO> exportList = new ArrayList<>(); Optional.ofNullable(list).ifPresent(po -> { po.stream().forEach(p -> { ExportExcelVO exportExcelVO = ExportExcelVO.builder() .uuid(p.getUuid()) .name(p.getName()) .status(p.getStatus()) .startTime(p.getStartTime()) .build(); exportList.add(exportExcelVO); }); }); //导出 try { writeExcel(response, exportList, "result", "表名", ExportExcelVO.class); } catch (Exception e) { e.printStackTrace(); log.error("错误信息Exception:", e); } log.info("执行完毕,导出成功"); }
4、导出实体类:
import com.alibaba.excel.annotation.ExcelIgnore; import com.alibaba.excel.annotation.ExcelProperty; import com.alibaba.excel.annotation.format.DateTimeFormat; import com.alibaba.excel.annotation.write.style.ColumnWidth; import com.alibaba.excel.annotation.write.style.ContentRowHeight; import com.alibaba.excel.annotation.write.style.HeadRowHeight; import lombok.Builder; import lombok.Data; import java.io.Serializable; @HeadRowHeight(value = 35) // 设置表头行高 @ContentRowHeight(value = 25) // 设置内容行高 //@ColumnWidth(value = 50) // 设置列宽 @Data @Builder public class ExportExcelVO implements Serializable { @ExcelProperty(value = {"编号"},order = 1) private String uuid; @ExcelProperty(value = {"基本信息","详情","名字"},order = 2) private String name; @ExcelProperty(value = {"基本信息","详情","状态"},order = 3) private String status; @ExcelProperty(value = {"基本信息","操作时间","时间"},order = 4) @DateTimeFormat("yyyy-MM-dd") //时间格式 private String startTime; }
5、SQL语句:
<select id="selectAllDemo" resultMap="BaseResultMap" parameterType="java.lang.String"> select <include refid="Base_Column_List"/> from page_demo where 1=1 <if test="id neq null and id != ''"> AND uuid = #{id} </if> <if test="ids != null and ids.size()!=0"> AND uuid in <foreach collection="ids" item="item" index="index" open="(" separator="," close=")"> #{item} </foreach> </if> </select>
6、导出样本:
4.3、EasyExcel的导入操作(模板下载与数据导入):
4.3.1、通用导入工具类ImportUtil:
import com.alibaba.excel.EasyExcel; import com.alibaba.excel.context.AnalysisContext; import com.alibaba.excel.event.AnalysisEventListener; import lombok.extern.slf4j.Slf4j; import org.springframework.web.multipart.MultipartFile; import java.io.InputStream; import java.util.ArrayList; import java.util.List; @Slf4j public class ImportUtil { /** * 导入 Excel * * @param multipartFile excel文件 * @param clazz 数据类型的class对象 * @param <T> * @return */ public static <T> List<T> importExcel(MultipartFile multipartFile, Class<T> clazz) throws Exception { List<T> list = new ArrayList<>(); InputStream inputStream = multipartFile.getInputStream(); EasyExcel.read(inputStream, clazz, new AnalysisEventListener<T>() { //每解析一行就会调用一次,data数据表示解析出来一行的数据 @Override public void invoke(T data, AnalysisContext context) { list.add(data); } //当全部数据读取完成后调用该方法 @Override public void doAfterAllAnalysed(AnalysisContext context) { log.info("解析完成"); } }).sheet().doRead(); return list; } }
4.3.2、业务流程:
1、控制类:
@Autowired private OperateService operateService; /** * 导入模板下载 * http://localhost:8081/v1/download * */ @GetMapping("/download") public void downloadTemplate(HttpServletResponse response) { operateService.importTemplate(response); } /** * 导入 * http://localhost:8081/v1/import-data */ @PostMapping(value="/import-data") public Response<List<ImportExcelVO>> importExcel(@RequestParam(value = "file") MultipartFile file) { List<ImportExcelVO> list = operateService.importData(file); return Response.success(list); }
2、业务类:
/** * 导入模板下载 */ @Override public void importTemplate(HttpServletResponse response) { List<ImportExcelVO> importExcelVO = new ArrayList<>(); try { //相当导出 Excel操作 writeExcel(response, importExcelVO, "importTemplate", "表名", ImportExcelVO.class); } catch (Exception e) { e.printStackTrace(); log.error("错误信息Exception:", e); } log.info("执行完毕,导出成功"); } /** * 导入操作 */ @Override public List<ImportExcelVO> importData(MultipartFile file) { List<ImportExcelVO> list = new ArrayList<>(); try { //获取数据 list = importExcel(file, ImportExcelVO.class); } catch (Exception e) { e.printStackTrace(); } //数据操作: for (ImportExcelVO importExcelVO : list) { log.info("输出:" + importExcelVO); } return list; }
4、导入实体类:
@HeadRowHeight(value = 35) // 设置表头行高 @ContentRowHeight(value = 25) // 设置内容行高 //@ColumnWidth(value = 50) // 设置列宽 @Data public class ImportExcelVO implements Serializable { @ExcelProperty(value = {"编号"},order = 1) private String uuid; @ExcelProperty(value = {"基本信息","详情","名字"},order = 2) private String name; @ExcelProperty(value = {"基本信息","详情","状态"},order = 3) private String status; @ExcelProperty(value = {"基本信息","操作时间","时间"},order = 4) @DateTimeFormat("yyyy-MM-dd") //时间格式 private String startTime; }
五、参考: