EasyExcel 导出 一步到位 开箱即用!!!

一、前言
开发中经常会设计到excel的处理,如导出Excel,导入Excel到数据库中!
首先我们先简单了解一下Excel:
Excel xls和xlsx有什么区别:
1、文件格式不同。xls 是一个特有的二进制格式,其核心结构是复合文档类型的结构,而 xlsx 的核心结构是 XML 类型的结构,采用的是基于 XML 的压缩方式,使其占用的空间更小。xlsx 中最后一个 x 的意义就在于此。

2、版本不同。xls是excel2003及以前版本生成的文件格式,而xlsx是excel2007及以后版本生成的文件格式。

3、兼容性不同。xlsx格式是向下兼容的,可兼容xls格式。

二、关于 Apache POI 和EsayExcel 的选择:
Apache POI 是通过内存一次性全部读取 然后在输出, ,EasyExcel 能大大减少占用内存的主要原因是在解析 Excel 时没有将文件数据一次性全部加载到内存中,而是从磁盘上一行行读取数据,逐个解析。阿里的大佬们在设计EsayExcel时 也是基于POI开发 不过使用起来比较轻便,通过注解和一行代码就可以解决Excel导入和导出。
下面我们来看看大佬们总结的两种方式实现Excel的解析过程图:
在这里插入图片描述
三、 我设计的方便的工具类 来完成写出 大家可以直接CV大法 就能使用 亲测有效

  <!--相关依赖:阿里Excel文件处理-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>easyexcel</artifactId>
            <version>2.2.6</version>
        </dependency>

1、EasyExcelUtils:

import com.alibaba.excel.EasyExcel;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;

/**
 * @author zhu
 * @date 2021/4/27 14:10
 * EasyExcel 工具 传入Excel必要的参数
 */
@Data
@Slf4j
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class EasyExcelUtil {
    
    

    /**
     * Excel下载文件名
     */
    private String excelName;

    /**
     * 工作表 ps:如果需要支持多表 请自定义添加对应的sheetName
     */
    private String sheetName;

    /**
     * Excel数据
     */
    private List<?> resultList;

    /**
     * EasyExcel 写出
     *
     * @param easyExcelUtil 自定义参数对象
     * @param clazz         指定返回表格对象 ps:用注解定义行和列
     * @throws UnsupportedEncodingException 编码异常  解决字符编码问题。
     */
    public static void create(EasyExcelUtil easyExcelUtil, Class<?> clazz) throws UnsupportedEncodingException {
    
    
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder
                .getRequestAttributes();
        //避免response报错空指针异常
        assert requestAttributes != null;
        HttpServletResponse httpServletResponse = requestAttributes.getResponse();
        //下载Excel名称
        String fileName = new String(
                (easyExcelUtil.getExcelName() + new SimpleDateFormat("yyyy-MM-dd").format(new Date()) + ".xlsx")
                        .getBytes(), StandardCharsets.UTF_8);
        //处理中文乱码
        fileName = new String(fileName.getBytes(), "ISO_8859_1");
        //导出Excel 设置两个头 响应内容格式
        //避免response报错空指针异常
        assert httpServletResponse != null;
        httpServletResponse.setContentType("application/vnd.ms-excel;charset=UTF-8");
        //设置前端下载文件名
        httpServletResponse.setHeader("Content-disposition", "attachment;filename=" + fileName);
        try {
    
    
            //向前端写入文件流流
            EasyExcel.write(httpServletResponse.getOutputStream(), clazz)
                    .sheet(easyExcelUtil.getSheetName())
                    //此步骤是开启EasyEXCEL自适应列宽
                    .registerWriteHandler(new CustomCellWriteHandler())
                    .doWrite(easyExcelUtil.getResultList());
        } catch (IOException e) {
    
    
            log.info("[EasyExcelUtil-create] Excel下载出错");
        }

    }

}

二、EasyEXCEL自适应列宽:

import com.alibaba.excel.enums.CellDataTypeEnum;
import com.alibaba.excel.metadata.CellData;
import com.alibaba.excel.metadata.Head;
import com.alibaba.excel.util.CollectionUtils;
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
import com.alibaba.excel.write.style.column.AbstractColumnWidthStyleStrategy;
import org.apache.poi.ss.usermodel.Cell;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author zhu
 * @date 2021/4/25 17:14
 * EasyEXCEL自适应列宽
 * 在导出时注册registerWriteHandler(new CustomCellWriteHandler())
 */
public class CustomCellWriteHandler extends AbstractColumnWidthStyleStrategy {
    
    
    private Map<Integer, Map<Integer, Integer>> CACHE = new HashMap<>();

    @Override
    protected void setColumnWidth(WriteSheetHolder writeSheetHolder, List<CellData> cellDataList, Cell cell, Head head, Integer integer, Boolean isHead) {
    
    
        boolean needSetWidth = isHead || !CollectionUtils.isEmpty(cellDataList);
        if (needSetWidth) {
    
    
            Map<Integer, Integer> maxColumnWidthMap = CACHE.get(writeSheetHolder.getSheetNo());
            if (maxColumnWidthMap == null) {
    
    
                maxColumnWidthMap = new HashMap<>();
                CACHE.put(writeSheetHolder.getSheetNo(), maxColumnWidthMap);
            }

            Integer columnWidth = this.dataLength(cellDataList, cell, isHead);
            if (columnWidth >= 0) {
    
    
                if (columnWidth > 255) {
    
    
                    columnWidth = 255;
                }

                Integer maxColumnWidth = maxColumnWidthMap.get(cell.getColumnIndex());
                if (maxColumnWidth == null || columnWidth > maxColumnWidth) {
    
    
                    maxColumnWidthMap.put(cell.getColumnIndex(), columnWidth);
                    writeSheetHolder.getSheet().setColumnWidth(cell.getColumnIndex(), columnWidth * 256);
                }

            }
        }
    }

    private Integer dataLength(List<CellData> cellDataList, Cell cell, Boolean isHead) {
    
    
        if (isHead) {
    
    
            return cell.getStringCellValue().getBytes().length;
        } else {
    
    
            CellData cellData = cellDataList.get(0);
            CellDataTypeEnum type = cellData.getType();
            if (type == null) {
    
    
                return -1;
            } else {
    
    
                switch (type) {
    
    
                    case STRING:
                        return cellData.getStringValue().getBytes().length;
                    case BOOLEAN:
                        return cellData.getBooleanValue().toString().getBytes().length;
                    case NUMBER:
                        return cellData.getNumberValue().toString().getBytes().length;
                    default:
                        return -1;
                }
            }
        }
    }
}

三、自定义类型转换器: ps:一个类型转换器只支持一个类型 不能在一个转换器里写多个方法实现
因为 implements Converter 我们自定义的都是 CellData 方法

import com.alibaba.excel.converters.Converter;
import com.alibaba.excel.enums.CellDataTypeEnum;
import com.alibaba.excel.metadata.CellData;
import com.alibaba.excel.metadata.GlobalConfiguration;
import com.alibaba.excel.metadata.property.ExcelContentProperty;

/**
 * @author zhu
 * @date 2021/4/25 14:01
 * EasyExcel 自定义转换器
 */
public class TypeConverter implements Converter<Integer> {
    
    
    /**
     * 类型(1-新增 2-扩科 3-续费 4-新增连报 5-新增体验)
     */
    private static final String NEW = "新增";
    private static final String EXTENSION = "扩科";
    private static final String RENEWAL = "续费";
    private static final String NEW_EVEN = "新增连报";
    private static final String NEW_EXPERIENCE = "新增体验";
    private static final String NULL_CHARACTER = " ";

    @Override
    public Class supportJavaTypeKey() {
    
    
        return Integer.class;
    }

    @Override
    public CellDataTypeEnum supportExcelTypeKey() {
    
    
        return CellDataTypeEnum.STRING;
    }

    @Override
    public Integer convertToJavaData(CellData cellData, ExcelContentProperty excelContentProperty,
            GlobalConfiguration globalConfiguration) throws Exception {
    
    
        return null;
    }

    @Override
    public CellData convertToExcelData(Integer num, ExcelContentProperty excelContentProperty,
            GlobalConfiguration globalConfiguration) throws Exception {
    
    

        switch (num) {
    
    
            case 1:
                return new CellData(NEW);
            case 2:
                return new CellData(EXTENSION);
            case 3:
                return new CellData(RENEWAL);
            case 4:
                return new CellData(NEW_EVEN);
            case 5:
                return new CellData(NEW_EXPERIENCE);
            default:
                return new CellData(NULL_CHARACTER);
        }
    }
}

四、测试:

1、返回Excel数据的实体类对象

import cn.njcool.backend.edu.common.utils.TypeConverter;
import com.alibaba.excel.annotation.ExcelIgnore;
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.annotation.format.DateTimeFormat;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Builder;
import lombok.Data;


/**
 * @author zhu
 * @date 2021/4/27 16:24
 */
@Data
@Builder
@ExcelIgnoreUnannotated
/**
 * @ExcelIgnoreUnannotated
 * 默认不加ExcelProperty 的注解的都会参与读写,加了不会参与
 * 所以我们在某些场景下不需要ID  用Excel的自动排序  就开启该注解
 */
public class StudentDO {
    
    
    /**
     * 默认所有字段都会和excel去匹配,加了这个注解会忽略该字段
     * 但是在类上需要开启@ExcelIgnoreUnannotated
     */
    @ExcelIgnore
    private Integer id;

    //第一列序号为0
    @ExcelProperty(value = "学员名称", index = 0)
    private String name;

    /**
     * @ExcelProperty(value = "学员名称", index = 0)
     * 指定当前字段对应excel中的那一列。可以根据名字或者Index去匹配。当然也可以不写,默认第一个字段就是index=0,
     * 以此类推。千万注意,要么全部不写,要么全部用index,要么全部用名字去匹配。千万别三个混着用,除非你非常了解源代码中三个混着用怎么去排序的。
     */
    @ExcelProperty(value = "学员爱好", index = 1)
    private String hobby;

    /**
     * 类型(1-新增 2-扩科 3-续费 4-新增连报 5-新增体验
     * 转换器,默认加载了很多转换器。也可以自定义  指向你自定义的转换器
     */
    @ExcelProperty(value = "类型", index = 2, converter = TypeConverter.class)
    private Integer type;
    /**
     * DateTimeFormat 日期转换,
     * 用String去接收excel日期格式的数据会调用这个注解。
     * 里面的value参照java.text.SimpleDateFormat
     */
    //    @ExcelProperty(value = "时间", index = 3)
    //    @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
    //    @DateTimeFormat(pattern = "yyyy-MM-dd")
    //    private String date;
}

2、控制层方法调用:
具体的业务逻辑可以和之前一样查询 如果rsulstList为空则返回空表数据

    @ApiOperation(value = "Excel下载测试")
    @GetMapping("test")
    public void Test() throws UnsupportedEncodingException {
    
    
        //就不查数据库了 一层撸到底 大家一下就看懂了
        //准备数据
        List<StudentDO> resultList = new ArrayList<>();
        StudentDO StudentDO1 = StudentDO.builder().id(1).name("哈拉少").hobby("洗澡的干活").type(1).build();
        StudentDO StudentDO2 = StudentDO.builder().id(2).name("龚大人").hobby("摸鱼").type(2).build();
        StudentDO StudentDO3 = StudentDO.builder().id(3).name("秋桑").hobby("钓鱼").type(3).build();
        StudentDO StudentDO4 = StudentDO.builder().id(4).name("文总").hobby("分析一波").type(4).build();
        StudentDO StudentDO5 = StudentDO.builder().id(5).name("为老总").hobby("18禁").type(5).build();
        resultList.add(StudentDO1);
        resultList.add(StudentDO2);
        resultList.add(StudentDO3);
        resultList.add(StudentDO4);
        resultList.add(StudentDO5);

        //构建生成Excel对象
        EasyExcelUtil excel = EasyExcelUtil.builder()
                .excelName("应天打工人")
                .sheetName("个人介绍")
                .resultList(resultList)
                .build();

        //Run
        EasyExcelUtil.create(excel,StudentDO.class);

    }

Ps: 小结用psotman调用 httpRespon 并不会被解析 我们需要用浏览器进行调用解析
在这里插入图片描述

下载完成之后的表: 已经执行了我们的自定义转换器
在这里插入图片描述
五、总结
目前还总结到导出 写入还没有了解释 不足之处请大家多批评指正 欢迎留言!!!

おすすめ

転載: blog.csdn.net/weixin_48134878/article/details/116203373