当前位置:   article > 正文

EasyExcel的导入导出使用_easyexcelfactory.write

easyexcelfactory.write


一点江南

已于 2023-01-18 11:10:23 修改

1164
 收藏 4
分类专栏: 文件相关 文章标签: excel
版权

文件相关
专栏收录该内容
4 篇文章1 订阅
订阅专栏
1、说明
EasyExcel是阿里出的一款基于Java的、快速、简洁、解决大文件内存溢出的处理Excel的开源工具,本文主要是使用这个工具对Excel导入导出进行讲解。

官网: https://easyexcel.opensource.alibaba.com/
github: https://github.com/alibaba/easyexcel
2、EasyExcel的使用
2.1、创建工具类
创建工具类EasyExcelUtil,将导出和导入的相关方法写入该类中,以下详细讲述使用EasyExcel导出、导入数据。

2.1.1、EasyExcel导出
2.1.1.1、导出一个sheet-无样式
/**
 * 导出excel表格
 *
 * @param response
 * @param dataList  数据列表
 * @param clazz     数据对象
 * @param fileName  文件名称
 * @param sheetName sheet名称
 * @throws Exception
 */
public static <T> void exportExcel(HttpServletResponse response, List<T> dataList, Class<T> clazz, String fileName, String sheetName) throws Exception {
    response.setContentType("application/vnd.ms-excel");
    response.setCharacterEncoding(StandardCharsets.UTF_8.name());
    fileName = URLEncoder.encode(fileName, StandardCharsets.UTF_8.name());
    response.setHeader("Content-disposition", "attachment;filename=" + fileName + FileTypeEnum.TEMPLATE_SUFFIX.getDesc());
    EasyExcelFactory.write(response.getOutputStream(), clazz).sheet(sheetName).doWrite(dataList);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2.1.1.2、导出一个sheet-有样式
/**
 * 导出excel表格-支设置单元格样式
 *
 * @param response
 * @param dataList     数据列表
 * @param clazz        数据对象
 * @param fileName     文件名称
 * @param sheetName    sheet名称
 * @param writeHandler 处理器
 * @throws Exception
 */
public static <T> void exportWriteHandlerExcel(HttpServletResponse response, List<T> dataList, Class<T> clazz, String fileName, String sheetName, WriteHandler writeHandler) throws Exception {
    response.setContentType("application/vnd.ms-excel");
    response.setCharacterEncoding(StandardCharsets.UTF_8.name());
    fileName = URLEncoder.encode(fileName, StandardCharsets.UTF_8.name());
    response.setHeader("Content-disposition", "attachment;filename=" + fileName + FileTypeEnum.TEMPLATE_SUFFIX.getDesc());
    ExcelWriter excelWriter = EasyExcel.write(response.getOutputStream(), clazz).build();
    // 这里注意如果同一个sheet只要创建一次
    WriteSheet writeSheet = EasyExcel.writerSheet(sheetName)
            .registerWriteHandler(writeHandler).build();
    excelWriter.write(dataList, writeSheet);
    excelWriter.finish();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
2.1.1.3、导出多个sheet
/**
 * 导出多个sheet
 *
 * @param response
 * @param dataList 多个sheet数据列表
 * @param clazzMap 对应每个sheet列表里面的数据对应的sheet名称
 * @param fileName 文件名
 * @param <T>
 * @throws Exception
 */
public static <T> void exportExcels(HttpServletResponse response, List<List<?>> dataList, Map<Integer, String> clazzMap, String fileName) throws Exception {
    response.setContentType("application/vnd.ms-excel");
    response.setCharacterEncoding(StandardCharsets.UTF_8.name());
    fileName = URLEncoder.encode(fileName, StandardCharsets.UTF_8.name());
    response.setHeader("Content-disposition", "attachment;filename=" + fileName + FileTypeEnum.TEMPLATE_SUFFIX.getDesc());
    ExcelWriter excelWriter = EasyExcel.write(response.getOutputStream()).build();
    int sheetNum = dataList.size();
    for (int i = 0; i < sheetNum; i++) {
        List<?> objects = dataList.get(i);
        Class<?> aClass = objects.get(0).getClass();
        WriteSheet writeSheet0 = EasyExcel.writerSheet(i, clazzMap.get(i)).head(aClass).build();
        excelWriter.write(objects, writeSheet0);
    }
    excelWriter.finish();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
2.1.1.4、导出手动填充数据的模板
此功能应用于导出同一个业务功能数据,但是每次导出的数据的表头是动态变化的,比如不同租户查看的同一个页面上展示的字段不一样,举个例子,租户A看到展示的字段是a、b、c;租户B看到展示字段是a、b。那么此时导出模板的表头字段就不一样。
<1>导出模板,传入一个处理器

/**
 * 导出excel表格-列不确定情况;只导出模板【一个处理器】
 *
 * @param response
 * @param headList     表头列表
 * @param dataList     数据列表
 * @param fileName     文件名称
 * @param sheetName    sheet名称
 * @param writeHandler 处理器
 * @throws Exception
 */
public static <T> void exportExcel(HttpServletResponse response, List<List<String>> headList, List<List<T>> dataList, 
String fileName, String sheetName, WriteHandler writeHandler) throws Exception {
    response.setContentType("application/vnd.ms-excel");
    response.setCharacterEncoding(StandardCharsets.UTF_8.name());
    fileName = URLEncoder.encode(fileName, StandardCharsets.UTF_8.name());
    response.setHeader("Content-disposition", "attachment;filename=" + fileName + FileTypeEnum.TEMPLATE_SUFFIX.getDesc());
    ExcelWriter excelWriter = EasyExcel.write(response.getOutputStream()).build();
    // 这里注意如果同一个sheet只要创建一次
    ExcelWriterSheetBuilder sheetBuilder = EasyExcel.writerSheet(sheetName).head(headList);
    if (null != writeHandler) {
        sheetBuilder.registerWriteHandler(writeHandler);
    }
    WriteSheet writeSheet = sheetBuilder.build();
    excelWriter.write(dataList, writeSheet);
    excelWriter.finish();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<2>导出模板,传入多个处理器

/**
 * 导出excel表格-列不确定情况;只导出模板【多个处理器】
 *
 * @param response
 * @param headList      表头列表
 * @param dataList      数据列表
 * @param fileName      文件名称
 * @param sheetName     sheet名称
 * @param writeHandlers 处理器集合
 * @throws Exception
 */
public static <T> void exportExcel(HttpServletResponse response, List<List<String>> headList, List<List<T>> dataList, String fileName, String sheetName, List<WriteHandler> writeHandlers) throws Exception {
    response.setContentType("application/vnd.ms-excel");
    response.setCharacterEncoding(StandardCharsets.UTF_8.name());
    fileName = URLEncoder.encode(fileName, StandardCharsets.UTF_8.name());
    response.setHeader("Content-disposition", "attachment;filename=" + fileName + FileTypeEnum.TEMPLATE_SUFFIX.getDesc());
    ExcelWriter excelWriter = EasyExcel.write(response.getOutputStream()).build();
    // 这里注意如果同一个sheet只要创建一次
    ExcelWriterSheetBuilder sheetBuilder = EasyExcel.writerSheet(sheetName).head(headList);
    if (CollUtil.isNotEmpty(writeHandlers)) {
        for (WriteHandler writeHandler : writeHandlers) {
            sheetBuilder.registerWriteHandler(writeHandler);
        }
    }
    WriteSheet writeSheet = sheetBuilder.build();
    excelWriter.write(dataList, writeSheet);
    excelWriter.finish();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
2.1.1.5、根据模板导出数据
填充数据的模板示例如下,注意:模板的格式需要加上字段对应的字段属性名称。样式举例:

说明:. 的{}就是根据自定义实体存值,{}里面是你自己写的字段
<1>填充列表数据

/**
 * 根据模板将集合对象填充表格
 *
 * @param inputStream 模板文件输入流
 * @param response    模板文件输出流
 * @param list        填充对象集合
 * @param fileName    文件名称
 * @param sheetName   需要写入的sheet(不传:填充到第一个sheet)
 * @throws Exception
 */
public static <T> void exportTemplateExcelList(InputStream inputStream, HttpServletResponse response, List<T> list, String fileName, String sheetName) throws Exception {
    // 全部填充:全部加载到内存中一次填充
    if (StringUtils.isBlank(sheetName)) {
        EasyExcelFactory.write(getOutputStream(fileName, response)).withTemplate(inputStream).sheet().doFill(list);
    } else {
        EasyExcelFactory.write(getOutputStream(fileName, response)).withTemplate(inputStream).sheet(sheetName).doFill(list);
    }
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<2>填充列表+自定义字段属性值数据
此场景适用于模板中的一些字段不是实体中字段,而是一些特殊处理的字段,比如说展示当前文档的一个指示说明,只是特别标注在文档中某一个位置就好了,这个不是集合中的数据,所以自定义字段需要进行特殊处理,他的处理是通过下面方法的入参object。

一个sheet

/**
 * 根据模板将集合对象填充表格-单个sheet
 *
 * @param inputStream 模板文件输入流
 * @param list        填充对象集合-元素对应模板中的.{}
 * @param object      填充对象-元素对应模板中的{}
 * @param fileName    文件名称
 * @throws Exception
 */
public static <T> void exportTemplateSheet(InputStream inputStream, HttpServletResponse response, List<T> list, Object object, String fileName) throws Exception {
    FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build();
    ExcelWriter excelWriter = EasyExcelFactory.write(getOutputStream(fileName, response)).withTemplate(inputStream).build();
    WriteSheet writeSheet0 = EasyExcelFactory.writerSheet(0).build();
    // 全部填充:全部加载到内存中一次填充
    excelWriter.fill(object, fillConfig, writeSheet0);
    excelWriter.fill(list, fillConfig, writeSheet0);
    // 可分多次填充,使用文件缓存(省内存)
    // excelWriter.fill(list1, writeSheet);
    // excelWriter.fill(list2, writeSheet);
    excelWriter.finish();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
两个sheet

/**
 * 根据模板将集合对象填充表格-多个sheet
 *
 * @param inputStream 模板文件输入
 * @param response    模板文件输出流
 * @param list1       填充对象集合-元素对应模板中的.{}
 * @param list2
 * @param object1     填充对象-元素对应模板中的{}
 * @param object2
 * @param fileName    文件名称
 * @throws Exception
 */
public static <T> void exportTemplateSheets(InputStream inputStream, HttpServletResponse response, List<T> list1, List<T> list2,
                                            Object object1, Object object2, String fileName) throws Exception {
    FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build();
    ExcelWriter excelWriter = EasyExcelFactory.write(getOutputStream(fileName, response)).withTemplate(inputStream).build();
    WriteSheet writeSheet0 = EasyExcelFactory.writerSheet(0).build();
    WriteSheet writeSheet1 = EasyExcelFactory.writerSheet(1).build();
    excelWriter.fill(object1, fillConfig, writeSheet0);
    excelWriter.fill(list1, fillConfig, writeSheet0);
    excelWriter.fill(object2, fillConfig, writeSheet1);
    excelWriter.fill(list2, fillConfig, writeSheet1);
    excelWriter.finish();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
2.1.1.6、补充导出公共方法getOutputStream
/**
 * 构建输出流
 *
 * @param fileName 文件名称
 * @param response 模板文件输出流
 * @param response
 * @return
 * @throws Exception
 */
private static OutputStream getOutputStream(String fileName, HttpServletResponse response) throws Exception {
    fileName = URLEncoder.encode(fileName, StandardCharsets.UTF_8.name());
    response.setContentType("application/vnd.ms-excel");
    response.setCharacterEncoding(StandardCharsets.UTF_8.name());
    response.setHeader("Content-Disposition", "attachment;filename=" + fileName + FileTypeEnum.TEMPLATE_SUFFIX.getDesc());
    return response.getOutputStream();
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2.1.2、EasyExcel导入
2.1.2.1、导入一个sheet
/**
 * 导入-读取一页sheet
 *
 * @param inputStream 文件流
 * @param clazz       数据对象
 * @param sheetName   要读取的sheet (不传,默认读取第一个sheet)
 * @throws Exception
 */
public static <T> List<T> importExcel(InputStream inputStream, Class<T> clazz, String sheetName, ReadExcelListener readExcelListener) throws Exception {
    ExcelReaderBuilder builder = EasyExcelFactory.read(inputStream, clazz, readExcelListener);
    if (StringUtils.isBlank(sheetName)) {
        builder.sheet().doRead();
    } else {
        builder.sheet(sheetName).doRead();
    }
    return readExcelListener.getList();
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2.1.2.2、导入多个sheet
 /**
     * 导入-读取多个sheet
     *
     * @param inputStream  文件流
     * @param sheetNum     需要读取的sheet个数(默认0开始,如果传入2,则读取0、1)
     * @param sheetObjList 每个sheet里面需要封装的对象(如果index为2,则需要传入对应的2个对象)
     * @param <T>
     * @return
     */
    public static <T> List<List<T>> importExcels(InputStream inputStream, int sheetNum, List<T> sheetObjList, ReadExcelListener<T> readExcelListener) throws Exception {
        List<List<T>> resultList = new LinkedList<>();
        for (int index = 0; index < sheetNum; index++) {
            Class<T> tClass = (Class<T>) sheetObjList.get(index).getClass();
            EasyExcelFactory.read(inputStream, tClass, readExcelListener).sheet(index).doRead();
            List<T> list = readExcelListener.getList();
            resultList.add(list);
        }
        return resultList;
    }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2.2、创建导出样式相关公共类
2.2.1、设置单元格宽度
CellWidthStyleHandler

package com.wm.easyexcel.util;

import cn.hutool.core.collection.CollUtil;
import com.alibaba.excel.enums.CellDataTypeEnum;
import com.alibaba.excel.metadata.data.CellData;
import com.alibaba.excel.metadata.data.WriteCellData;
import com.alibaba.excel.write.handler.context.CellWriteHandlerContext;
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;

/**
 * @ClassName: CellWidthStyleHandler
 * @Description: 设置表头的调整宽策略
 * @Author: WM
 * @Date: 2022/12/23 14:37
 */
public class CellWidthStyleHandler extends AbstractColumnWidthStyleStrategy {

    // 可以根据这里的最大宽度,按自己需要进行调整,搭配单元格样式实现类中的,自动换行,效果更好
    private static final int MAX_COLUMN_WIDTH = 50;
    private Map<Integer, Map<Integer, Integer>> CACHE = new HashMap(8);

    /**
     * 是否设置固定宽度
     */
    private boolean fixed;

    /**
     * 固定宽度
     */
    private int fixedWidth;

    public CellWidthStyleHandler() {
    }

    public CellWidthStyleHandler(boolean fixed, int fixedWidth) {
        this.fixed = fixed;
        this.fixedWidth = (fixedWidth == 0 ? 15 : fixedWidth);
    }

    @Override
    protected void setColumnWidth(CellWriteHandlerContext context) {
        boolean needSetWidth = context.getHead() || !CollUtil.isEmpty(context.getCellDataList());
        if (!needSetWidth) {
            return;
        }
        WriteSheetHolder writeSheetHolder = context.getWriteSheetHolder();
        // 设置固定宽度
        if (fixed) {
            Cell cell = context.getCell();
            writeSheetHolder.getSheet().setColumnWidth(cell.getColumnIndex(), fixedWidth * 256);
            return;
        }
        // 设置自动调整宽度
        Map<Integer, Integer> maxColumnWidthMap = CACHE.get(writeSheetHolder.getSheetNo());
        if (maxColumnWidthMap == null) {
            maxColumnWidthMap = new HashMap(16);
            CACHE.put(writeSheetHolder.getSheetNo(), maxColumnWidthMap);
        }
        Cell cell = context.getCell();
        int columnWidth = this.dataLength(context.getCellDataList(), cell, context.getHead());
        if (columnWidth >= 0) {
            if (columnWidth > MAX_COLUMN_WIDTH) {
                columnWidth = MAX_COLUMN_WIDTH;
            }
            Integer maxColumnWidth = maxColumnWidthMap.get(cell.getColumnIndex());
            if (maxColumnWidth == null || columnWidth > maxColumnWidth) {
                maxColumnWidthMap.put(cell.getColumnIndex(), columnWidth);
                writeSheetHolder.getSheet().setColumnWidth(cell.getColumnIndex(), columnWidth * 256);
            }
        }
    }

    private int dataLength(List<WriteCellData<?>> 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;
                }
            }
        }
    }
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
2.2.2、设置默认单元格字体样式
DefaultExcelHeadHandler

package com.wm.easyexcel.util;

import com.alibaba.excel.metadata.data.WriteCellData;
import com.alibaba.excel.write.handler.context.CellWriteHandlerContext;
import com.alibaba.excel.write.metadata.style.WriteCellStyle;
import com.alibaba.excel.write.metadata.style.WriteFont;
import com.alibaba.excel.write.style.HorizontalCellStyleStrategy;
import org.apache.poi.ss.usermodel.IndexedColors;

/**
 * @ClassName: DefaultExcelHeadHandler
 * @Description: 默认自定义表头样式拦截器
 * @Author: WM
 * @Date: 2022/12/22 17:35
 */
public class DefaultExcelHeadHandler extends HorizontalCellStyleStrategy {

    //设置头样式
    @Override
    protected void setHeadCellStyle(CellWriteHandlerContext context) {
        WriteCellStyle headWriteCellStyle = new WriteCellStyle();
        headWriteCellStyle.setFillForegroundColor(IndexedColors.WHITE.getIndex());
        WriteFont headWriteFont = new WriteFont();
        headWriteFont.setFontHeightInPoints((short) 11);
        headWriteFont.setColor(IndexedColors.BLACK.getIndex());
        headWriteCellStyle.setWriteFont(headWriteFont);
        if (stopProcessing(context)) {
            return;
        }
        WriteCellData<?> cellData = context.getFirstCellData();
        WriteCellStyle.merge(headWriteCellStyle, cellData.getOrCreateStyle());
    }
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
ExcelHeadStyles

package com.wm.easyexcel.util;

import lombok.Data;

/**
 * @ClassName: ExcelHeadStyles
 * @Description: excel表头颜色定义
 * @Author: WM
 * @Date: 2022/12/22 17:24
 */
@Data
public class ExcelHeadStyles {

    /**
     * 表头横坐标 - 行
     */
    private Integer rowIndex;

    /**
     * 表头纵坐标 - 列
     */
    private Integer columnIndex;

    /**
     * 内置颜色
     */
    private Short indexColor;

    /**
     * 字体颜色
     */
    private Short fontColor;

    public ExcelHeadStyles(Integer columnIndex, Short fontColor) {
        this.columnIndex = columnIndex;
        this.fontColor = fontColor;
    }

    public ExcelHeadStyles(Integer rowIndex, Integer columnIndex, Short fontColor) {
        this.rowIndex = rowIndex;
        this.columnIndex = columnIndex;
        this.fontColor = fontColor;
    }
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
2.3、创建导入公共监听器
ReadExcelListener

package com.wm.easyexcel.util;

import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.exception.ExcelDataConvertException;
import lombok.extern.slf4j.Slf4j;

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

/**
 * @ClassName: ReadExcelListener
 * @Description: 读取excel文件数据监听器
 * @Author: WM
 * @Date: 2022/12/21 17:00
 */
@Slf4j
public abstract class ReadExcelListener<T> extends AnalysisEventListener<T> {

    private static final int BATCH_COUNT = 10000;

    /**
     * 数据集
     */
    private final List<T> list = new ArrayList<>();

    /**
     * 表头数据
     */
    private Map<Integer, String> headMap = null;

    private Map<String, Object> params;

    // 获取数据集
    public List<T> getList() {
        return this.list;
    }

    // 获取表头数据
    public Map<Integer, String> getHeadMap() {
        return this.headMap;
    }

    /**
     * 每条数据都会进入
     *
     * @param object
     * @param analysisContext
     */
    @Override
    public void invoke(T object, AnalysisContext analysisContext) {
        this.list.add(object);
        // 达到BATCH_COUNT了,需要去存储一次数据库,防止数据几万条数据在内存,容易OOM
        if (list.size() >= BATCH_COUNT) {
            saveData();
            // 存储完成清理 list
            list.clear();
        }
    }

    /**
     * 数据解析完调用
     *
     * @param analysisContext
     */
    @Override
    public void doAfterAllAnalysed(AnalysisContext analysisContext) {
        // 这里也要保存数据,确保最后遗留的数据也存储到数据库
        saveData();
        log.info("所有数据解析完成!");
    }

    /**
     * 读取到的表头信息
     *
     * @param headMap
     * @param context
     */
    @Override
    public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
        this.headMap = headMap;
    }

    /**
     * 异常时调用
     *
     * @param exception:
     * @param context:
     * @throws Exception
     */
    @Override
    public void onException(Exception exception, AnalysisContext context) throws Exception {
        // 数据解析异常
        if (exception instanceof ExcelDataConvertException) {
            ExcelDataConvertException excelDataConvertException = (ExcelDataConvertException) exception;
            throw new RuntimeException("第" + excelDataConvertException.getRowIndex() + "行" + excelDataConvertException.getColumnIndex() + "列" + "数据解析异常");
        }
        // 其他异常...
    }

    /**
     * 数据存储到数据库
     */
    public abstract boolean saveData();
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
这里作为公共导入监听器,保存数据的方法saveData(),下放到各层具体的业务去继承实现。

2.4、创建实体类
创建学生和老师两个实体类
Student

package com.wm.easyexcel.entity;

import com.alibaba.excel.annotation.ExcelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * @ClassName: Student
 * @Description: 学生
 * @Author: WM
 * @Date: 2021-07-22 21:30
 **/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Student {
    @ExcelProperty("序号")
    private Integer num;
    @ExcelProperty("学号")
    private String sno;
    @ExcelProperty("学生名字")
    private String name;
    @ExcelProperty("性别")
    private String sex;
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
Teacher

package com.wm.easyexcel.entity;

import com.alibaba.excel.annotation.ExcelProperty;
import lombok.Data;

/**
 * @ClassName: Teacher
 * @Description: 老师
 * @Author: WM
 * @Date: 2021-07-22 21:30
 **/
@Data
public class Teacher {
    @ExcelProperty("编号")
    private String sno;
    @ExcelProperty("老师名字")
    private String name;
    @ExcelProperty("性别")
    private String sex;
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
2.5、创建业务接口
IStudentService

package com.wm.easyexcel.service;

import com.wm.easyexcel.entity.Student;

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

/**
 * @ClassName: IStudentService
 * @Description: 学生相关接口
 * @Author: WM
 * @Date: 2023/1/9 16:52
 */
public interface IStudentService {

    /**
     * 查询学生集合信息
     *
     * @return
     */
    List<Student> listStudent(Long limit);

    /**
     * 批量保存学生数据
     *
     * @param students
     * @return
     */
    boolean batchSaveStudent(List<Student> students);

    /**
     * 批量保存学生数据-自行处理行列数据,不使用实体
     *
     * @param dataList
     * @param headMap
     * @return
     */
    boolean batchSaveStudent(List<Map<Integer, String>> dataList, Map<Integer, String> headMap);
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
对应的实现类StudentServiceImpl

package com.wm.easyexcel.service.impl;

import cn.hutool.core.lang.UUID;
import com.wm.easyexcel.entity.Student;
import com.wm.easyexcel.service.IStudentService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

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

/**
 * @ClassName: StudentServiceImpl
 * @Description: 学生相关接口实现
 * @Author: WM
 * @Date: 2023/1/9 16:53
 */
@Slf4j
@Service
public class StudentServiceImpl implements IStudentService {

    @Override
    public List<Student> listStudent(Long limit) {
        List<Student> students = new ArrayList<>();
        for (int i = 0; i < limit; i++) {
            Student student = new Student();
            student.setSex("男");
            student.setNum(i);
            student.setName("小A" + i);
            student.setSno(UUID.fastUUID().toString());
            students.add(student);
        }
        return students;
    }

    @Override
    public boolean batchSaveStudent(List<Student> students) {
        // 调用dao层接口保存数据...
        log.info("批量保存学生数据,共:{}条数据", students.size());
        return true;
    }

    @Override
    public boolean batchSaveStudent(List<Map<Integer, String>> dataList, Map<Integer, String> headMap) {
        // 这里可以针对读取到的表头数据和对应的行数据,这种应用场景适用于导入的excel中数据需要我们自己解析做其他业务处理,或者表头列是动态的,我们
        // 没有办法使用一个固定的实体做映射
        log.info("表头列数据:{}", headMap.values());
        log.info("批量保存学生数据,共:{}条数据", dataList.size());
        return true;
    }
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
ITeacherService

package com.wm.easyexcel.service;

import com.wm.easyexcel.entity.Teacher;

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

/**
 * @ClassName: ITeacherService
 * @Description: 老师相关接口
 * @Author: WM
 * @Date: 2023/1/9 16:52
 */
public interface ITeacherService {

    /**
     * 查询老师集合信息
     *
     * @return
     */
    List<Teacher> listTeacher(Long limit);

    /**
     * 批量保存老师数据
     *
     * @param Teachers
     * @return
     */
    boolean batchSaveTeacher(List<Teacher> Teachers);

}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
对应的实现类TeacherServiceImpl

package com.wm.easyexcel.service.impl;

import cn.hutool.core.lang.UUID;
import com.wm.easyexcel.entity.Teacher;
import com.wm.easyexcel.service.ITeacherService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

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

/**
 * @ClassName: TeacherServiceImpl
 * @Description: 老师相关接口实现
 * @Author: WM
 * @Date: 2023/1/9 16:53
 */
@Slf4j
@Service
public class TeacherServiceImpl implements ITeacherService {

    @Override
    public List<Teacher> listTeacher(Long limit) {
        List<Teacher> teachers = new ArrayList<>();
        for (int i = 0; i < limit; i++) {
            Teacher teacher = new Teacher();
            teacher.setSex("女");
            teacher.setName("老师A" + i);
            teacher.setSno(UUID.fastUUID().toString());
            teachers.add(teacher);
        }
        return teachers;
    }

    @Override
    public boolean batchSaveTeacher(List<Teacher> teachers) {
        // 调用dao层接口保存数据...
        log.info("批量保存老师数据,共:{}条数据", teachers.size());
        return true;
    }
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
2.6、创建Controller
2.6.1、导出ExportController
创建导出的Controller为工具类中各个导出方法的测试做准备。

2.6.1.1、代码展示
package com.wm.easyexcel.controller;

import cn.hutool.core.collection.CollUtil;
import com.wm.easyexcel.entity.Student;
import com.wm.easyexcel.handler.UserExcelHeadHandler;
import com.wm.easyexcel.service.IStudentService;
import com.wm.easyexcel.service.ITeacherService;
import com.wm.easyexcel.strategy.RowMergeStrategy;
import com.wm.easyexcel.util.CellWidthStyleHandler;
import com.wm.easyexcel.util.EasyExcelUtil;
import com.wm.easyexcel.util.ExcelHeadStyles;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.usermodel.IndexedColors;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @ClassName: ExportController
 * @Description: 导出前端控制器
 * @Author: WM
 * @Date: 2023/1/9 16:44
 */
@Slf4j
@RestController
public class ExportController {

    @Resource
    private IStudentService studentService;
    @Resource
    private ITeacherService teacherService;

    private static final String[] USER_HEAD_FIELDS = new String[]{"序号", "学号", "姓名", "性别"};

    /**
     * 导出学生数据-一个sheet
     *
     * @param response
     */
    @GetMapping("/student/export/sheet")
    public void exportSheetStudent(HttpServletResponse response) {
        List<Student> students = studentService.listStudent(10L);
        log.info("单sheet学生数据开始导出...");
        long startTimeL = System.currentTimeMillis();
        try {
            EasyExcelUtil.exportExcel(response, students, Student.class, "导出学生数据-一个sheet", "第一页");
        } catch (Exception e) {
            log.error("单sheet学生数据导出异常!", e);
        }
        log.info("单sheet学生数据导出完成:{}秒", (System.currentTimeMillis() - startTimeL) / 1000);
    }

    /**
     * 导出学生数据-多个sheet
     *
     * @param response
     */
    @GetMapping("/student/export/sheets")
    public void exportSheetsStudent(HttpServletResponse response) {
        log.info("多sheet学生数据开始导出...");
        long startTimeL = System.currentTimeMillis();
        try {
            List<List<?>> sheetList = new ArrayList<>();
            List<Student> students1 = studentService.listStudent(10L);
            List<Student> students2 = studentService.listStudent(20L);
            sheetList.add(students1);
            sheetList.add(students2);
            Map<Integer, String> clazzMap = new HashMap<>();
            clazzMap.put(0, "sheet1");
            clazzMap.put(1, "sheet2");
            EasyExcelUtil.exportExcels(response, sheetList, clazzMap, "导出学生数据-多个sheet");
        } catch (Exception e) {
            log.error("多sheet学生数据导出失败!", e);
        }
        log.info("多sheet学生数据导出完成:{}秒", (System.currentTimeMillis() - startTimeL) / 1000);
    }


    /**
     * 导出学生数据-重复行合并格式
     *
     * @param response
     */
    @GetMapping("/student/export/mergeRow")
    public void exportMergeRowStudent(HttpServletResponse response) {
        log.info("合并格式学生数据开始导出...");
        long startTimeL = System.currentTimeMillis();
        try {
            List<Student> students = studentService.listStudent(10L);
            List<String> mergeDataList = students.stream().map(Student::getSex).collect(Collectors.toList());
            EasyExcelUtil.exportWriteHandlerExcel(response, students, Student.class, "学生重复数据表格合并", "第一页", new RowMergeStrategy(mergeDataList, 3));
        } catch (Exception e) {
            log.error("合并格式学生数据导出失败!", e);
        }
        log.info("合并格式学生数据导出完成:{}秒", (System.currentTimeMillis() - startTimeL) / 1000);
    }

    /**
     * 下载导入学生数据的模板【一个自定义Handler】
     *
     * @param response
     */
    @GetMapping("/student/downTemplate001")
    public void downTemplateStudent001(HttpServletResponse response) {
        log.info("设置单元格宽度-学生数据模板开始下载...");
        long startTimeL = System.currentTimeMillis();
        try {
            List<List<String>> headList = Arrays.stream(USER_HEAD_FIELDS).map(field -> CollUtil.newArrayList(field)).
                    collect(Collectors.toList());
            CellWidthStyleHandler columnWidthHandler = new CellWidthStyleHandler(true, 15);
            EasyExcelUtil.exportExcel(response, headList, Collections.EMPTY_LIST, "学生数据导入模板-设置单元格宽度", "student", columnWidthHandler);
        } catch (Exception e) {
            log.error("设置单元格宽度-学生数据模板下载失败!", e);
        }
        log.info("设置单元格宽度-学生数据模板下载完成:{}秒", (System.currentTimeMillis() - startTimeL) / 1000);
    }

    /**
     * 下载导入学生数据的模板【多个自定义Handler】
     *
     * @param response
     */
    @GetMapping("/student/downTemplate002")
    public void downTemplateStudent002(HttpServletResponse response) {
        log.info("设置单元格宽度+字体-学生数据模板开始下载...");
        long startTimeL = System.currentTimeMillis();
        try {
            List<List<String>> headList = Arrays.stream(USER_HEAD_FIELDS).map(field -> CollUtil.newArrayList(field)).
                    collect(Collectors.toList());
            // 表头第一行的指定前3列字标红
            ExcelHeadStyles excelHeadStyle = new ExcelHeadStyles(0, 3, IndexedColors.RED1.getIndex());
            UserExcelHeadHandler headHandler = new UserExcelHeadHandler(excelHeadStyle);
            CellWidthStyleHandler columnWidthHandler = new CellWidthStyleHandler(true, 15);
            EasyExcelUtil.exportExcel(response, headList, Collections.EMPTY_LIST, "学生数据导入模板-设置单元格宽度+字体", "student", CollUtil.newArrayList(headHandler, columnWidthHandler));
        } catch (Exception e) {
            log.error("设置单元格宽度+字体-学生数据模板下载失败!", e);
        }
        log.info("设置单元格宽度+字体-学生数据模板下载完成:{}秒", (System.currentTimeMillis() - startTimeL) / 1000);
    }


    /**************以下填充模板数据的接口测试说明:可改为get请求,InputStream可使用本地文件输入流,然后变可通过浏览器下载。******************************/
    /**
     * 根据模板将集合对象填充表格-list
     *
     * @param template
     * @param response
     */
    @PostMapping("/student/template/fill-list")
    public void exportFillTemplateList(@RequestParam("template") MultipartFile template, HttpServletResponse response) {
        log.info("根据模板填充学生数据,list,文件开始下载...");
        long startTimeL = System.currentTimeMillis();
        try {
            List students = studentService.listStudent(10L);
            EasyExcelUtil.exportTemplateExcelList(template.getInputStream(), response, students, "填充学生模板数据-list", "students");
        } catch (Exception e) {
            log.error("根据模板填充学生数据,list,文件下载失败!", e);
        }
        log.info("根据模板填充学生数据,list,文件下载完成:{}秒", (System.currentTimeMillis() - startTimeL) / 1000);
    }

    /**
     * 根据模板将集合对象填充表格-单个sheet
     *
     * @param template
     * @param response
     */
    @PostMapping("/student/template/fill-sheet")
    public void exportFillTemplateSheet(@RequestParam("template") MultipartFile template, HttpServletResponse response) {
        log.info("根据模板填充学生数据,单个sheet,文件开始下载...");
        long startTimeL = System.currentTimeMillis();
        try {
            List<Student> students = studentService.listStudent(10L);
            Map<String, Object> map = new HashMap<>(3);
            map.put("startMonth", "2022-8-10");
            map.put("endMonth", "2022-8-12");
            EasyExcelUtil.exportTemplateSheet(template.getInputStream(), response, students, map, "填充学生模板数据-sheet");
        } catch (Exception e) {
            log.error("根据模板填充学生数据,单个sheet,文件下载失败!", e);
        }
        log.info("根据模板填充学生数据,单个sheet,文件下载完成:{}秒", (System.currentTimeMillis() - startTimeL) / 1000);
    }

    /**
     * 根据模板将集合对象填充表格-多个sheet
     *
     * @param template
     * @param response
     */
    @PostMapping("/student/template/fill-sheets")
    public void exportFillTemplateSheets(@RequestParam("template") MultipartFile template, HttpServletResponse response) {
        log.info("根据模板填充数据,多个sheet,文件开始下载...");
        long startTimeL = System.currentTimeMillis();
        try {
            List students = studentService.listStudent(10L);
            Map<String, Object> map = new HashMap<>(2);
            map.put("startMonth", "2022-8-10");
            map.put("endMonth", "2022-8-12");
            List teachers = teacherService.listTeacher(10L);
            Map<String, Object> map2 = new HashMap<>(1);
            map2.put("illustrate", "老师人员列表");
            EasyExcelUtil.exportTemplateSheets(template.getInputStream(), response, students, teachers, map, map2, "填充学生模板数据-sheets");
        } catch (Exception e) {
            log.error("根据模板填充数据,多个sheet,文件下载失败!", e);
        }
        log.info("根据模板填充数据,多个sheet,文件下载完成:{}秒", (System.currentTimeMillis() - startTimeL) / 1000);
    }

    /********转为get请求测试示例********/
    /**
     * get请求测试
     *
     * @param response
     */
    @GetMapping("/student/template/test")
    public void test(HttpServletResponse response) throws FileNotFoundException {
        File file = new File("C:\\Users\\Administrator\\Desktop\\test.xlsx");
        FileInputStream inputStream = new FileInputStream(file);
        log.info("根据模板填充学生数据,单个sheet,文件开始下载...");
        long startTimeL = System.currentTimeMillis();
        try {
            List<Student> students = studentService.listStudent(10L);
            Map<String, Object> map = new HashMap<>(3);
            map.put("startMonth", "2022-8-10");
            map.put("endMonth", "2022-8-12");
            EasyExcelUtil.exportTemplateSheet(inputStream, response, students, map, "填充学生模板数据-sheet");
        } catch (Exception e) {
            log.error("根据模板填充学生数据,单个sheet,文件下载失败!", e);
        }
        log.info("根据模板填充学生数据,单个sheet,文件下载完成:{}秒", (System.currentTimeMillis() - startTimeL) / 1000);
    }
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
2.6.1.2、方法说明
(1)导出学生数据-一个sheet
此方法是简单的做导出数据,导出的excel不做任何格式的修改。
导出结果如下:


(2)导出学生数据-多个sheet
此方法导出两个sheet的数据,导出的excel不做任何格式的修改。
导出结果如下:


(3)导出学生数据-重复行合并格式
此方法导出数据中,指定列的相邻行单元格有重复的内容会被合并成一个单元格。这里需要创建一个设定单元格合并策略类RowMergeStrategy:

package com.wm.easyexcel.strategy;

import cn.hutool.core.collection.CollUtil;
import com.alibaba.excel.metadata.Head;
import com.alibaba.excel.write.merge.AbstractMergeStrategy;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.util.CellRangeAddress;

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

/**
 * @ClassName: RowMergeStrategy
 * @Description: 自定义行合并策略
 * @Author: WM
 * @Date: 2023/1/9 20:01
 */
public class RowMergeStrategy extends AbstractMergeStrategy {

    /**
     * 分组,每几行合并一次
     */
    private List<Integer> exportFieldGroupCountList;

    /**
     * 目标合并列index
     */
    private Integer targetColumnIndex;

    // 需要开始合并单元格的首行index
    private Integer rowIndex;

    // exportDataList为待合并目标列的值
    public RowMergeStrategy(List<String> exportDataList, Integer targetColumnIndex) {
        this.exportFieldGroupCountList = getGroupCountList(exportDataList);
        this.targetColumnIndex = targetColumnIndex;
    }

    @Override
    protected void merge(Sheet sheet, Cell cell, Head head, Integer integer) {
        if (null == rowIndex) {
            rowIndex = cell.getRowIndex();
        }
        // 仅从首行以及目标列的单元格开始合并,忽略其他
        if (cell.getRowIndex() == rowIndex && cell.getColumnIndex() == targetColumnIndex) {
            mergeGroupColumn(sheet);
        }
    }

    private void mergeGroupColumn(Sheet sheet) {
        int rowCount = rowIndex;
        for (Integer count : exportFieldGroupCountList) {
            if (count == 1) {
                rowCount += count;
                continue;
            }
            // 合并单元格
            CellRangeAddress cellRangeAddress = new CellRangeAddress(rowCount, rowCount + count - 1, targetColumnIndex, targetColumnIndex);
            sheet.addMergedRegionUnsafe(cellRangeAddress);
            rowCount += count;
        }
    }

    // 该方法将目标列根据值是否相同连续可合并,存储可合并的行数
    private List<Integer> getGroupCountList(List<String> exportDataList) {
        if (CollUtil.isEmpty(exportDataList)) {
            return new ArrayList<>();
        }
        List<Integer> groupCountList = new ArrayList<>();
        int count = 1;
        for (int i = 1; i < exportDataList.size(); i++) {
            if (exportDataList.get(i).equals(exportDataList.get(i - 1))) {
                count++;
            } else {
                groupCountList.add(count);
                count = 1;
            }
        }
        groupCountList.add(count);
        return groupCountList;
    }
}


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
导出结果如下:


(4)下载导入学生数据的模板【一个自定义Handler】
这个方法的应用场景是,用户下载自己需要填充数据的模板,这里excel的表头是自定义设定的,可固定也可不固定,我程序里设定的是固定写死的,举个不固定表头的例子:不同租户下用户导出的模板显示的表头数据不同,这个一般是在数据库查出来的设置的。
此外这个方法还设置了单元格的固定宽度样式,所以使用了一个Handler处理器,即CellWidthStyleHandler。
导出结果如下:
(5)下载导入学生数据的模板【多个自定义Handler】
此方法是在(4)方法的基础上,通过ExcelHeadStyles、UserExcelHeadHandler增加了对表头字体的设置,这里处理的是表头第一行的指定前3列字标红。因为是这是指定业务个人化的一种样式,所以不放入公共样式包里。
UserExcelHeadHandler代码如下:

package com.wm.easyexcel.handler;

import com.alibaba.excel.metadata.data.WriteCellData;
import com.alibaba.excel.write.handler.context.CellWriteHandlerContext;
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.wm.easyexcel.util.ExcelHeadStyles;
import org.apache.poi.ss.usermodel.IndexedColors;

/**
 * @ClassName: UserExcelHeadHandler
 * @Description: 用户相关excel处理器
 * @Author: WM
 * @Date: 2023/1/9 18:24
 */
public class UserExcelHeadHandler extends HorizontalCellStyleStrategy {

    private ExcelHeadStyles excelHeadStyle;


    public UserExcelHeadHandler(ExcelHeadStyles excelHeadStyle) {
        this.excelHeadStyle = excelHeadStyle;
    }

    //设置头样式
    @Override
    protected void setHeadCellStyle(CellWriteHandlerContext context) {
        int columnNo = excelHeadStyle.getColumnIndex();
        WriteCellStyle headWriteCellStyle = new WriteCellStyle();
        headWriteCellStyle.setFillForegroundColor(IndexedColors.WHITE.getIndex());
        WriteFont headWriteFont = new WriteFont();
        headWriteFont.setFontHeightInPoints((short) 11);
        if (context.getColumnIndex() <= columnNo) {
            headWriteFont.setColor(excelHeadStyle.getFontColor());
        } else {
            headWriteFont.setColor(IndexedColors.BLACK.getIndex());
        }
        headWriteCellStyle.setWriteFont(headWriteFont);
        if (stopProcessing(context)) {
            return;
        }
        WriteCellData<?> cellData = context.getFirstCellData();
        WriteCellStyle.merge(headWriteCellStyle, cellData.getOrCreateStyle());
    }
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
导出结果如下:


(6)根据模板将集合对象填充表格-list
此方法是根据上传的模板文件,将查询的数据填充到excel指定的名称的sheet,然后导出。
上传文件的模板(后面根据上传模板导出填充数据都是用的这个模板):

模板影样式如下:
导出结果如下:


(7)根据模板将集合对象填充表格-单个sheet
此方法涉及到自定义字段数据的导出,这里是“开始日期”、“结束日期”。
模板样式如下:

导出结果如下:


(8)根据模板将集合对象填充表格-多个sheet
此方法导出两个不同集合的数据,指定到对应的sheet。
学生的样式未变,老师模板格式如下:

导出结果如下:


2.6.2、导入ImportController
创建导入的Controller为工具类中各个导入方法的测试做准备。

2.6.2.1、代码展示
package com.wm.easyexcel.controller;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Assert;
import com.wm.easyexcel.entity.Student;
import com.wm.easyexcel.entity.Teacher;
import com.wm.easyexcel.listern.ImportUserListener;
import com.wm.easyexcel.util.EasyExcelUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;

/**
 * @ClassName: ImportController
 * @Description: 导入前端控制器
 * @Author: WM
 * @Date: 2023/1/9 16:44
 */
@Slf4j
@RestController
public class ImportController {

    @Resource
    private ImportUserListener importUserListener;

    /**
     * 导入一个sheet学生数据
     *
     * @param file
     */
    @PostMapping("/student/import/sheet")
    public boolean importSheetStudent(@RequestParam("file") MultipartFile file) {
        EasyExcelUtil.checkFile(file);
        log.info("一个sheet学生数据开始导入...");
        long startTime = System.currentTimeMillis();
        List<Student> dataList = null;
        try {
            dataList = EasyExcelUtil.importExcel(file.getInputStream(), null, null, importUserListener);
            // 数据录入
            Assert.isFalse(CollUtil.isEmpty(dataList), "未解析到导入的数据!");
        } catch (Exception e) {
            log.error("一个sheet学生数据导入失败!", e);
        }
        log.info("一个sheet学生数据导入成功,共:{}条,消耗总时间:{}秒", dataList.size(), ((System.currentTimeMillis() - startTime) / 1000));
        return true;
    }

    /**
     * 导入多个sheet学生数据
     *
     * @param file
     */
    @PostMapping("/student/import/sheets")
    public boolean importSheets(@RequestParam("file") MultipartFile file) {
        EasyExcelUtil.checkFile(file);
        log.info("多个sheet数据开始导入...");
        long startTime = System.currentTimeMillis();
        List<Student> dataList = null;
        try {
            List sheetObjList = new ArrayList(2);
            sheetObjList.add(Student.class);
            sheetObjList.add(Teacher.class);
            dataList = EasyExcelUtil.importExcels(file.getInputStream(), 2, sheetObjList, importUserListener);
            // 数据录入
            Assert.isFalse(CollUtil.isEmpty(dataList), "未解析到导入的数据!");
        } catch (Exception e) {
            log.error("多个sheet数据导入失败!", e);
        }
        log.info("多个sheet数据导入成功,共:{}条,消耗总时间:{}秒", dataList.size(), ((System.currentTimeMillis() - startTime) / 1000));
        return true;
    }
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
2.6.2.2、方法说明
(1)导入一个sheet学生数据
此方法是将上传的学生数据excel文件解析,然后解析后的数据保存到数据库中。此处需要使用到的ImportUserListener继承了ReadExcelListener,其主要实现逻辑是将学生或老师的数据进行保存处理。
ImportUserListener代码如下:

package com.wm.easyexcel.listern;

import cn.hutool.core.collection.CollUtil;
import com.wm.easyexcel.entity.Student;
import com.wm.easyexcel.entity.Teacher;
import com.wm.easyexcel.service.IStudentService;
import com.wm.easyexcel.service.ITeacherService;
import com.wm.easyexcel.util.ReadExcelListener;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.List;

/**
 * @ClassName: ImportStudentListener
 * @Description: 导入学生excel数据监听器
 * @Author: WM
 * @Date: 2022/12/22 10:44
 */
@Component
public class ImportUserListener extends ReadExcelListener {

    @Resource
    private IStudentService studentService;
    @Resource
    private ITeacherService teacherService;

    /**
     * 实现数据保存逻辑
     *
     * @return
     */
    @Override
    public boolean saveData() {
        List dataList = getList();
        if (CollUtil.isEmpty(dataList)) {
            return false;
        }
        Object obj = dataList.get(0);
        if (obj instanceof Student) {
            return studentService.batchSaveStudent(dataList);
        } else if (obj instanceof Teacher) {
            return teacherService.batchSaveTeacher(dataList);
        }
        return true;
    }
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
(2)导入多个sheet学生数据
此方法是将excel解析出来的学生和老师数据集合保存于数据库中,这里其实就是在(1)方法的基础上增加了循环处理的操作。

2.6.2.3、导入监听器-动态表头字段
有些场景每次导入数据的表头字段是动态变化的,所以我们不能使用一个固定的实体去和excel表头字段做映射,那么这时候就需要我们自己去处理表头和表数据。这里使用到了ImportDataListener监听器,代码如下:

package com.wm.easyexcel.listern;

import cn.hutool.core.collection.CollUtil;
import com.wm.easyexcel.service.IStudentService;
import com.wm.easyexcel.util.ReadExcelListener;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.List;
import java.util.Map;

/**
 * @ClassName: ImportDataListener
 * @Description: 导入数据监听器
 * @Author: WM
 * @Date: 2023/1/9 19:27
 */
@Component
public class ImportDataListener extends ReadExcelListener {

    @Resource
    private IStudentService studentService;

    /**
     * 实现数据保存逻辑-直接操作表头和行数据
     *
     * @return
     */
    @Override
    public boolean saveData() {
        Map<Integer, String> headMap = getHeadMap();
        List dataList = getList();
        if (CollUtil.isEmpty(headMap) || CollUtil.isEmpty(dataList)) {
            return false;
        }
        return studentService.batchSaveStudent(dataList, headMap);
    }
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
3、测试
3.1、导出测试
3.1.1、总测试结果
最终导出的文件如下所示:


3.2、导入测试
3.2.1、导入一个sheet学生数据


3.2.2、导入多个sheet学生数据


4、总结
本文主要是对EasyExcel导入导出使用的讲解,包括一些导出常用的单元格宽度以及内部字体样式设置,常规的导出以及不固定表头数据的导出,根据上传模板导出数据;常规的根据上传excel文件导入数据,以及根据非固定字段的excel文件上传导入数据。
本文章的demo地址:https://github.com/Deamer1102/excel-demo/tree/master/easyexcel
————————————————
版权声明:本文为CSDN博主「一点江南」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weimenglala/article/details/128712422

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/代码吟游诗人/article/detail/61048
推荐阅读
相关标签
  

闽ICP备14008679号