平时我们导入导出Excel的时候如果用poi导出,会发现光设置格式都要很多代码,看起来非常的不优雅。后来业务中遇到了需要导入非常巨大的Excel的需求。如果继续用poi的方式,因为poi把所有excel数据都缓存到内存中,服务器资源又是有限的,所以就有可能导致内存溢出,为了解决这个问题,我发现阿里的EasyExcel导出可以完美的解决这个问题,并且实现方式更加优雅。
如果业务中需要标红重复的数据,先列一下大致代码。
/** * 导出重复的数据 * * @param planId * @param stream * @param tenaId * @param fileId * @param response * @throws IOException */ public void screeningExport(Long planId, InputStream stream, Long tenaId, Long fileId, HttpServletResponse response) throws IOException { response.setContentType("application/vnd.ms-excel"); response.setCharacterEncoding("utf-8"); // 这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系 String fileName = URLEncoder.encode("重复项", "UTF-8"); response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx"); EasyExcel.write(response.getOutputStream(), ScreeningEasyExcelVo.class) .registerWriteHandler(new CustomCellWriteHandle()) .sheet("模板" + Constant.fileIdSeparator + fileId).doWrite(data(stream, planId, tenaId, fileId)); }
大致的来说,就是我们传入excel文件流,和对应的业务字段就可以导出了。
但是如果想标红某一单元格的数据,那我们就需要自己定义拦截器,同时我们需要知道到底是哪行是重复的,具体校验重复行就不在这里写了,
得到重复行之后,我们把行数存入redis,并且在拦截器中和excel中的行数做对比,如果验证为同一行,那我们就可以对这一行做标红处理,
具体的代码如下
package com.guantong.seeing.screening.common; import com.alibaba.excel.metadata.CellData; import com.alibaba.excel.metadata.Head; import com.alibaba.excel.write.handler.CellWriteHandler; import com.alibaba.excel.write.metadata.holder.WriteSheetHolder; import com.alibaba.excel.write.metadata.holder.WriteTableHolder; import com.guantong.seeing.screening.util.SpringContextHelper; import org.apache.poi.ss.usermodel.*; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.util.StringUtils; import java.util.List; import java.util.stream.Stream; /** * @author cuixinxin * @desc easyExcel 自定义拦截器 */ public class CustomCellWriteHandle implements CellWriteHandler { public CustomCellWriteHandle() { } @Override public void beforeCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, Head head, Integer integer, Integer integer1, Boolean aBoolean) { } @Override public void afterCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Cell cell, Head head, Integer integer, Boolean aBoolean) { } /** * 重写单元格格式--》通过修改每一个单元格格式达到整列标红的效果 * @param writeSheetHolder * @param writeTableHolder * @param list * @param cell * @param head * @param integer * @param aBoolean */ @Override public void afterCellDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, List<CellData> list, Cell cell, Head head, Integer integer, Boolean aBoolean) { Workbook workbook = writeSheetHolder.getSheet().getWorkbook(); CellStyle cellStyle = cellStyle(workbook); //根据校验结果设置单元格文字颜色 String sheetName = writeSheetHolder.getSheet().getSheetName(); Long fileId = Long.valueOf(sheetName.substring(sheetName.indexOf(Constant.fileIdSeparator) + 8)); writeSheetHolder.setSheetName(sheetName.substring(0, sheetName.indexOf(Constant.fileIdSeparator))); int rowIndex = cell.getRowIndex(); if (isExistRepeatData(rowIndex, fileId)) { //设置单元格背景色 Font font = workbook.createFont(); font.setColor(IndexedColors.RED.getIndex()); cellStyle.setFont(font); } cell.setCellStyle(cellStyle); } private boolean isExistRepeatData(Integer rowNow, Long fileId) { StringRedisTemplate stringRedisTemplate = SpringContextHelper.getBean(StringRedisTemplate.class); String value = stringRedisTemplate.opsForValue().get(fileId.toString()); if (StringUtils.isEmpty(value)) { return false; } return Stream.of(value.split(",")) .anyMatch(k -> k.equals(rowNow.toString())); } /** * 通用样式 * * @param workbook * @return */ public static CellStyle cellStyle(Workbook workbook) { CellStyle cellStyle = workbook.createCellStyle(); //居中 cellStyle.setAlignment(HorizontalAlignment.LEFT); cellStyle.setVerticalAlignment(VerticalAlignment.CENTER); cellStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND); cellStyle.setFillForegroundColor(IndexedColors.WHITE.getIndex()); //设置边框 cellStyle.setBorderBottom(BorderStyle.THIN); cellStyle.setBorderLeft(BorderStyle.THIN); cellStyle.setBorderRight(BorderStyle.THIN); cellStyle.setBorderTop(BorderStyle.THIN); return cellStyle; } }
整体来看,代码逻辑分工更加清晰,只有标红的拦截器和获取数据以及导出三部分,更加优雅