SpringBoot+EasyExcel导入导出加水印

Posted 经理,天台风好大

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SpringBoot+EasyExcel导入导出加水印相关的知识,希望对你有一定的参考价值。


本篇文章内容是针对于接口开发~~

《POI方式导出导入的参考链接》


背景

老项目主要采用的POI框架来进行Excel数据的导入和导出,但经常会出现OOM的情况,导致整个服务不可用。后续逐步转移到EasyExcel。

一、EasyExcel介绍

EasyExcel是阿里巴巴开源poi插件之一,主要解决了poi框架使用复杂,sax解析模式不容易操作,数据量大起来容易OOM,解决了POI并发造成的报错。

主要解决方式:通过解压文件的方式加载,一行一行的加载,并且抛弃样式字体等不重要的数据,降低内存的占用。

EasyExcel优势:

  • 注解式自定义操作。
  • 输入输出简单,提供输入输出过程的接口
  • 支持一定程度的单元格合并等灵活化操作

官方文档地址:https://easyexcel.opensource.alibaba.com/

其他详细介绍参考官网

二、案例配置

2.1 依赖

<!-- 后续给Excel加水印的时候会用到 -->
<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>ooxml-schemas</artifactId>
    <version>1.0</version>
</dependency>

<!--easyExcel-->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>easyexcel</artifactId>
    <version>2.2.11</version>
</dependency>

<!-- hutool -->
<dependency>
    <groupId>cn.hutool</groupId>
    <artifactId>hutool-all</artifactId>
    <version>5.4.0</version>
</dependency>

<!-- web -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

<!-- lombok -->
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
</dependency>

其他所需依赖自行添加

2.2 导入导出的实体类

@Data
@ToString
@AllArgsConstructor
@NoArgsConstructor
public class Member 
    /**
     * EasyExcel使用:导出时忽略该字段
     */
    @ExcelIgnore
    private Integer id;

    @ExcelProperty("用户名")
    @ColumnWidth(20)
    private String username;

    /**
     * EasyExcel使用:日期的格式化
     */
    @ColumnWidth(20)
    @ExcelProperty("出生日期")
    @DateTimeFormat("yyyy-MM-dd")
    private Date birthday;

    /**
     * EasyExcel使用:自定义转换器
     */
    @ColumnWidth(10)
    @ExcelProperty(value = "性别", converter = GenderConverter.class)
    private Integer gender;

常用注解:

@ExcelProperty(index = 0, value = “姓名”) :

  • 用于设置Excel表头,其中index用户表头的编号,从0开始;value为表头对应的内容。

@ExcelProperty(value = “性别”, converter = GenderConverter.class)

  • 自定义转换器

@DateTimeFormat(“yyyy-MM-dd”)

  • 用于日期的格式化。

@ContentStyle(dataFormat = 2)

  • 保留两位小数

排除指定列的三种方式:

  • 方式1:在类上添加 @ExcelIgnoreUnannotated
  • 方式2:指定字段加@ExcelIgnore注解
  • 方式3:EasyExcel.write(fileName, UserData.class).sheet(“学生信息表”)
    .excludeColumnFiledNames(Arrays.asList(“remark”)).doWrite(getData());
    这种方法的好处是:同一Excel可以在调用方法时排除不同的数据列。

2.3 自定义性别转换器

自定义内容转换器,类似枚举的实现,将“男”、“女”转换成“0”、“1”的数值。

package com.daniel.utils;

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 daniel
 * @createTime 2022/9/16 0016 10:25
 * @description
 */

public class GenderConverter implements Converter<Integer> 

    private static final String MAN = "男";
    private static final String WOMAN = "女";


    @Override
    public Class<?> supportJavaTypeKey() 
        // 实体类中对象属性类型
        return Integer.class;
    

    @Override
    public CellDataTypeEnum supportExcelTypeKey() 
        // Excel中对应的CellData属性类型
        return CellDataTypeEnum.STRING;
    

    @Override
    public Integer convertToJavaData(CellData cellData, ExcelContentProperty excelContentProperty,
                                     GlobalConfiguration globalConfiguration) 
        // 从Cell中读取数据
        String gender = cellData.getStringValue();
        // 判断Excel中的值,将其转换为预期的数值
        if (MAN.equals(gender)) 
            return 0;
         else if (WOMAN.equals(gender)) 
            return 1;
        
        return null;
    

    @Override
    public CellData<?> convertToExcelData(Integer integer, ExcelContentProperty excelContentProperty,
                                          GlobalConfiguration globalConfiguration) 
        // 判断实体类中获取的值,转换为Excel预期的值,并封装为CellData对象
        if (integer == null) 
            return new CellData<>("");
         else if (integer == 0) 
            return new CellData<>(MAN);
         else if (integer == 1) 
            return new CellData<>(WOMAN);
        
        return new CellData<>("");
    


三、导入导出工具类

3.1 监听类

package com.daniel.utils;

import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * @author daniel
 * @createTime 2022/9/16 0016 11:04
 * @description 解析监听器
 *   每解析一行会回调invoke()方法。
 *   整个excel解析结束会执行doAfterAllAnalysed()方法
 *
 *   没有考虑合并单元格的情况
 */

public class ExcelListener extends AnalysisEventListener<Object> 

    //定义一个保存Excel所有记录的集合
    private List<Object> datas = new ArrayList<>();

    public List<Object> getDatas() 
        return datas;
    

    public void setDatas(List<Object> datas) 
        this.datas = datas;
    

    /**
     * 逐行解析
     * object : 当前行的数据
     * 这个每一条数据解析都会来调用
     * 我们将每一条数据都保存到list集合中
     * @param object    one row value. Is is same as @link AnalysisContext#readRowHolder()
     * @param analysisContext
     */
    @Override
    public void invoke(Object object, AnalysisContext analysisContext) 
        System.out.println("读取object=" + object);
        //当前行
        // context.getCurrentRowNum()
        //数据存储到list,供批量处理,或后续自己业务逻辑处理。
        if (object != null) 
            datas.add(object);
//            handleBusinessLogic();
        

          /*
        如数据过大,可以进行定量分批处理
        if(datas.size() >= 200)
            handleBusinessLogic();
            datas.clear();
        
         */
    

    /**
     * 读取表头内容
     * @param headMap 表头
     * @param analysisContext
     */
    @Override
    public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext analysisContext) 
        System.out.println("表头" + headMap);
    

    /**
     * 解析完所有数据后会调用该方法
     * @param analysisContext
     */
    @Override
    public void doAfterAllAnalysed(AnalysisContext analysisContext) 
        //解析结束销毁不用的资源,非必要语句,查看导入的数据
        System.out.println("读取Excel完毕" + datas.size());
    

    //根据业务自行实现该方法,例如将解析好的dataList存储到数据库中
    private void handleBusinessLogic() 

3.2 ExcelUtils工具类

package com.daniel.utils;

import cn.hutool.core.convert.Convert;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelReader;
import com.alibaba.excel.read.metadata.ReadSheet;
import com.alibaba.excel.write.metadata.style.WriteCellStyle;
import com.alibaba.excel.write.style.HorizontalCellStyleStrategy;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;

/**
 * @author daniel
 * @createTime 2022/9/16 0016 9:47
 * @description
 */
public class ExcelUtils 

    private static final Logger log = LoggerFactory.getLogger(ExcelUtils.class);

    /**
     * 读取Excel(多个sheet可以用同一个实体类解析)
     * @param excelInputStream
     * @param fileName
     * @param clazz
     * @param <T>
     * @return
     */
    public static <T> List<T> readExcel(InputStream excelInputStream, String fileName, Class<T> clazz) 
        ExcelListener excelListener = new ExcelListener();
        ExcelReader excelReader = getReader(excelInputStream, fileName,clazz, excelListener);
        if (excelReader == null) 
            return new ArrayList<>();
        
        List<ReadSheet> readSheetList = excelReader.excelExecutor().sheetList();
        for (ReadSheet readSheet : readSheetList) 
            excelReader.read(readSheet);
        
        excelReader.finish();
        return Convert.toList(clazz, excelListener.getDatas());
    

    /**
     * 导出数据
     *
     * @param head      类名
     * @param excelname excel名字
     * @param data      数据
     */
    public static void getExcelimporttemplate(Class head, String excelname, List data) 
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletResponse response = requestAttributes.getResponse();
        // 这里注意 有人反应使用swagger 会导致各种问题,请直接用浏览器或者用postman
        //response.setContentType("application/vnd.ms-excel");
        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        response.setCharacterEncoding("utf-8");

        // 这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系
        try 
            //表格头部样式
            WriteCellStyle headWriteCellStyle = new WriteCellStyle();
            //设置背景颜色
            //headWriteCellStyle.setFillForegroundColor(IndexedColors.WHITE.getIndex());
            //设置头字体
            //WriteFont headWriteFont = new WriteFont();
            //headWriteFont.setFontHeightInPoints((short)13);
            //headWriteFont.setBold(true);
            //headWriteCellStyle.setWriteFont(headWriteFont);
            //设置头居中
            headWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);

            //表格内容样式
            WriteCellStyle contentWriteCellStyle = new WriteCellStyle();
            //设置 水平居中
            contentWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);

            HorizontalCellStyleStrategy horizontalCellStyleStrategy = new HorizontalCellStyleStrategy(headWriteCellStyle, contentWriteCellStyle);
            String fileName = URLEncoder.encode(excelname, "UTF-8").replaceAll("\\\\+", "%20");
            response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");
            EasyExcel.write(response.getOutputStream(), head).registerWriteHandler(horizontalCellStyleStrategy).sheet("Sheet1").doWrite(data);
         catch (UnsupportedEncodingException e) 
            e.printStackTrace();
         catch (IOException e) 
            e.printStackTrace();
        
    

    /**
     * 导出数据
     *
     * @param head      类名
     * @param excelname excel名字
     * @param data      数据
     */
    public static void excelExport(Class head, String excelname, List data) 
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletResponse response = requestAttributes.getResponse();
        // 这里注意 有人反应使用swagger 会导致各种问题,请直接用浏览器或者用postman
        //response.setContentType("application/vnd.ms-excel");
        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        response.setCharacterEncoding("utf-8");
        // 这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系
        try 
            String fileName = URLEncoder.encode(excelname, "UTF-8").replaceAll("\\\\+", "%20");
            response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");
            EasyExcel.write(response.getOutputStream(), head).sheet("Sheet1").doWrite(data);
         catch (UnsupportedEncodingException e) 
            e.printStackTrace();
         catch (IOException e) 
            e.printStackTrace();
        
    
    
    /**
     * 导出数据二级表头
     * (合并第一行单元格,第二行开始是业务字段)
     * @param bigTitle  合并单元格的名字
     * @param head      类名
     * @param excelname 导出的excel名字
     * @param data      数据
     * @param header    导出的字段列名
     */
    public static void getBigTitleExcel(String bigTitle, Class head, String excelname, List data, List<String> header) 
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletResponse response = requestAttributes.getResponse();
        // 这里注意 有人反应使用swagger 会导致各种问题,请直接用浏览器或者用postman
        //response.setContentType("application/vnd.ms-excel");
        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        //response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
        response.setCharacterEncoding("utf-8");

        // 这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系
        try 
            //表格头部样式
            WriteCellStyle headWriteCellStyle = new WriteCellStyle();
            //设置背景颜色
            //headWriteCellStyle.setFillForegroundColor(IndexedColors.WHITE.getIndex());
            //设置头字体
            //WriteFont headWriteFont = new WriteFont();
            //headWriteFont.setFontHeightInPoints((short)13);
            //headWriteFont.setBold(true);
            //headWriteCellStyle.setWriteFont(headWriteFont);
            //设置头居中
            headWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);

            //表格内容样式
            WriteCellStyle contentWriteCellStyle = new WriteCellStyle();
            //设置 水平居中
            contentWriteCellStyle.setHorizontalAlignment(HorizontalAlignment<

以上是关于SpringBoot+EasyExcel导入导出加水印的主要内容,如果未能解决你的问题,请参考以下文章

SpringBoot+EasyExcel导入导出加水印

Springboot整合easyExcel导入导出Excel

使用VUE+SpringBoot+EasyExcel 整合导入导出数据

Java:SpringBoot使用EasyExcel实现Excel文件的导出下载和上传导入功能

SpringBoot基于EasyExcel解析Excel实现文件导出导入读取写入

SpringBoot+POI方式导出excel加水印