Java导出Excel文件合集(easyExcel)

目录

前言

一、EasyExcel简介

1、网站

二、EasyExcel使用

1、相关依赖

2、实现代码

2.1 实体类

2.2 服务层

2.2.1 服务接口类

扫描二维码关注公众号,回复: 16034563 查看本文章

2.2.2 实现类

2.3 控制层

2.4 相关工具类

2.4.1 导出Util

2.4.2 Excel自定义样式拦截器

2.4.3 Excel单元格拦截器

2.4.4 Excel自定义列宽拦截器

2.4.5 性别转换器

2.4.6 性别枚举

2.5 结果

三、EasyExcel常用注解

1、@ExcelProperty

2、@ColumnWith

3、@ContentFontStyle

4、@ContentLoopMerge和@OnceAbsoluteMerge

4.1 @ContentLoopMerge

4.2 @OnceAbsoluteMerge

5、@ContentRowHeight

6、@ContentStyle

7、@HeadFontStyle

8、@HeadRowHeight

9、@HeadStyle

10、@ExcelIgnore

11、@ExcelIgnoreUnannotated

12、@NumberFormat

13、@DateTimeFormat


前言

        相信在大部分的web项目中都会有导出导入Excel的需求,今天我们就来看看如何用阿里的EasyExcel组件去实现导出Excel文件的需求。之前我也写过几篇使用Apache POI导出Excel的文章,有兴趣的小伙伴可以去瞅瞅,链接也给大家放出来了:

1、Apache POI导出excel(一):单sheet

2、Apache POI导出excel(二):多个sheet

3、JAVA 导出excel(三):导出zip压缩包


一、EasyExcel简介

        Java解析、生成Excel比较有名的框架有Apache POIJXL。但他们都存在一个严重的问题就是非常的耗内存,POI有一套SAX模式的API可以一定程度的解决一些内存溢出的问题,但POI还是有一些缺陷,比如07版Excel解压缩以及解压后存储都是在内存中完成的,内存消耗依然很大。
EasyExcel重写了POI对07版Excel的解析,一个3M的excel用POI sax解析依然需要100M左右内存,改用EasyExcel可以降低到几M,并且再大的excel也不会出现内存溢出;03版依赖POI的sax模式,在上层做了模型转换的封装,让使用者更加简单方便。

1、网站

二、EasyExcel使用

1、相关依赖

pom.xml

        <!-- easyExcel -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>easyexcel</artifactId>
            <version>3.2.1</version>
            <!-- 3+以上版本的easyExcel,使用poi 5+以上版本时,需要手动排除:poi-ooxml-schemas -->
            <exclusions>
                <exclusion>
                    <artifactId>poi-ooxml-schemas</artifactId>
                    <groupId>org.apache.poi</groupId>
                </exclusion>
            </exclusions>
        </dependency>

2、实现代码

2.1 实体类

import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.annotation.format.DateTimeFormat;
import com.alibaba.excel.annotation.write.style.ColumnWidth;
import com.test.java.converter.GenderConverter;
import lombok.Data;

import java.io.Serializable;
import java.util.Date;

/**
 * 学生类
 */
@Data // lombok注解,会生成getter/setter方法
@ExcelIgnoreUnannotated // 没加导出注解的字段,不导出
public class StudentVo implements Serializable {

    /*** 用户ID*/
    private Long userId;

    /*** 姓名*/
    @ExcelProperty(value = "姓名")
    @ColumnWidth(10)
    private String studentName;

    /*** 年龄*/
    @ExcelProperty(value = "年龄")
    private Integer age;

    /*** 手机号*/
    @ExcelProperty(value = "手机号")
    private String phone;

    /*** 性别(1男 2女)*/
    @ExcelProperty(value = "性别", converter = GenderConverter.class)
    private Integer gender;

    /*** 生日*/
    @ExcelProperty(value = "生日")
    private String birthday;

    /*** 分数*/
    @ExcelProperty(value = "分数")
    @NumberFormat(value = "###.#") // 数字格式化,保留1位小数
    private BigDecimal score;

    /*** 创建时间*/
    @ExcelProperty(value = "创建时间")
    @DateTimeFormat("yyyy-MM-dd")
    private Date createTime;

}

2.2 服务层

2.2.1 服务接口类

    // 导出学生信息
    List<StudentVo> exportStudent();

2.2.2 实现类

    @Override
    public List<StudentVo> exportStudent() {

        List<StudentVo> list = new ArrayList<>();
        // 我这里使用for循环 创造了10天测试数据,实际的业务场景肯定是从数据库中查出需要导出的数据
        for (int i = 1; i <= 10; i++) {
            StudentVo student = new StudentVo();
            student.setUserId((long) i);
            student.setStudentName("王" + i);
            student.setAge(18 + i);
            student.setPhone("1305678111" + i);
            if (i % 2 == 0) {
                student.setGender(2);
            } else {
                student.setGender(1);
            }
            student.setBirthday("1997-01-01");
            student.setCreateTime(new Date());
            list.add(student);
        }
        return list;
    }

2.3 控制层

    /**
     * 导出学生信息
     */
    @GetMapping("/exportStudent")
    public void exportStudent(HttpServletResponse response) {
        List<StudentVo> list = userService.exportStudent();

        // 指定列导出
        String column = "studentName,age,phone";// 定义无需导出的列字段
        if (StringUtils.isNotEmpty(column)) {
            List<String> columns = Arrays.asList(column.split(","));
            ExportUtil.exportExcel(response, StudentVo.class, "学生信息", list, columns);
        } else {
            ExportUtil.exportExcel(response, StudentVo.class, "学生信息", list);
        }
    }

PS:我这里写了两个导出方法。一个是正常导出全部;另一个是指定列导出,只需传入无需导出的列字段集合,导出的时候就会进行过滤导出。

2.4 相关工具类

2.4.1 导出Util

import com.alibaba.excel.EasyExcelFactory;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.read.listener.ReadListener;
import com.alibaba.excel.write.metadata.WriteSheet;

import javax.servlet.http.HttpServletResponse;
import java.io.InputStream;
import java.net.URLEncoder;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * Excel工具类
 */
public class ExportUtil extends EasyExcelFactory {

    public ExportUtil() {
    }

    /**
     * 导入Excel
     */
    public static void importExcel(InputStream inputStream, Class<?> head, ReadListener readListener) {
        read(inputStream, head, readListener).sheet().doRead();
    }

    /**
     * 导出Excel(全部)
     *
     * @param response 响应
     * @param clazz    表头数据
     * @param fileName 文件名
     * @param list     需要导出的数据
     */
    public static void exportExcel(HttpServletResponse response, Class<?> clazz, String fileName, List<?> list) {
        baseExportExcel(response, clazz, fileName, list, new ArrayList<>());
    }

    /**
     * 导出Excel(指定列导出)
     *
     * @param response       响应
     * @param clazz          表头数据
     * @param fileName       文件名
     * @param list           需要导出的数据
     * @param excludeColumns 过滤导出的字段名
     */
    public static void exportExcel(HttpServletResponse response, Class<?> clazz, String fileName, List<?> list, List<String> excludeColumns) {
        baseExportExcel(response, clazz, fileName, list, excludeColumns);
    }

    public static void baseExportExcel(HttpServletResponse response, Class<?> clazz, String fileName, List<?> list, List<String> excludeColumns) {
        try {
            SimpleDateFormat format = new SimpleDateFormat("yyyyMMddHHmmss");
            String name = format.format(new Date());
            String filename = URLEncoder.encode(name + fileName + ".xlsx", "UTF-8");
            response.setCharacterEncoding("utf-8");
            response.setContentType("application/vnd.ms-excel");
            response.setHeader("Content-disposition", "attachment;filename=" + filename);
            ExcelWriter excelWriter = write(response.getOutputStream()).registerWriteHandler(new ExcelColumnWidthHandler()).registerWriteHandler(new ExcelSheetWriteHandler()).registerWriteHandler(new ExcelStyleHandler()).excludeColumnFieldNames(excludeColumns).build();
            WriteSheet writeSheet = writerSheet(0, fileName).head(clazz).build();
            excelWriter.write(list, writeSheet);
            excelWriter.finish();
        } catch (Exception var8) {
            throw new RuntimeException("导出" + fileName + "失败");
        }
    }

    /**
     * 导出Excel(多个sheet导出)
     *
     * @param response 响应
     * @param fileName 文件名
     */
    public static ExcelWriter exportExcels(HttpServletResponse response, String fileName) {
        try {
            SimpleDateFormat format = new SimpleDateFormat("yyyyMMddHHmmss");
            String name = format.format(new Date());
            String filename = URLEncoder.encode(name + fileName + ".xlsx", "UTF-8");
            response.setCharacterEncoding("utf-8");
            response.setContentType("application/vnd.ms-excel");
            response.setHeader("Content-disposition", "attachment;filename=" + filename);
            return write(response.getOutputStream()).registerWriteHandler(new ExcelColumnWidthHandler()).registerWriteHandler(new ExcelSheetWriteHandler()).registerWriteHandler(new ExcelStyleHandler()).build();
        } catch (Exception var3) {
            throw new RuntimeException("导出" + fileName + "失败");
        }
    }

    /**
     * 创建工作表sheet
     *
     * @param sheetNo   工作表编号
     * @param sheetName 工作表名称
     * @param clazz     表头数据
     */
    public static WriteSheet createSheet(Integer sheetNo, String sheetName, Class<?> clazz) {
        return writerSheet(sheetNo, sheetName).head(clazz).build();
    }

}

2.4.2 Excel自定义样式拦截器

import com.alibaba.excel.metadata.Head;
import com.alibaba.excel.write.metadata.style.WriteCellStyle;
import com.alibaba.excel.write.metadata.style.WriteFont;
import com.alibaba.excel.write.style.AbstractVerticalCellStyleStrategy;
import org.apache.poi.ss.usermodel.BorderStyle;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.apache.poi.ss.usermodel.IndexedColors;

/**
 * Excel自定义样式拦截器
 */
public class ExcelStyleHandler extends AbstractVerticalCellStyleStrategy {

    private static final String FONT_NAME = "等线";
    private static final Integer[] COLUMN_INDEX = new Integer[]{1, 2, 3, 4, 7, 8, 9};


    @Override
    protected WriteCellStyle contentCellStyle(Head head) {
        // 内容的策略
        WriteCellStyle contentWriteCellStyle = new WriteCellStyle();
        contentWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);
        // 背景白色
        contentWriteCellStyle.setFillForegroundColor(IndexedColors.WHITE.getIndex());
        contentWriteCellStyle.setBorderLeft(BorderStyle.THIN);
        contentWriteCellStyle.setBorderTop(BorderStyle.THIN);
        contentWriteCellStyle.setBorderRight(BorderStyle.THIN);
        contentWriteCellStyle.setBorderBottom(BorderStyle.THIN);
        WriteFont contentWriteFont = new WriteFont();
        // 字体大小
        contentWriteFont.setFontHeightInPoints((short) 12);
        // 字体样式
        contentWriteFont.setFontName(FONT_NAME);
        contentWriteCellStyle.setWriteFont(contentWriteFont);
        return contentWriteCellStyle;
    }

}

2.4.3 Excel单元格拦截器

import com.alibaba.excel.write.handler.SheetWriteHandler;
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
import com.alibaba.excel.write.metadata.holder.WriteWorkbookHolder;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.xssf.streaming.SXSSFSheet;

/**
 * Excel单元格拦截器
 */
public class ExcelSheetWriteHandler implements SheetWriteHandler {

    // 设置100列column
    private static final Integer COLUMN = 100;

    @Override
    public void beforeSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) {
    }

    @Override
    public void afterSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) {
        for(int i = 0; i < COLUMN; ++i) {
            // 设置为文本格式
            SXSSFSheet sxssfSheet = (SXSSFSheet)writeSheetHolder.getSheet();
            CellStyle cellStyle = writeWorkbookHolder.getCachedWorkbook().createCellStyle();
            // 49为文本格式
            cellStyle.setDataFormat((short)49);
            // i为列,一整列设置为文本格式
            sxssfSheet.setDefaultColumnStyle(i, cellStyle);
        }
    }
}

2.4.4 Excel自定义列宽拦截器

import com.alibaba.excel.metadata.Head;
import com.alibaba.excel.metadata.data.WriteCellData;
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.List;

/**
 * Excel自定义列宽拦截器
 */
public class ExcelColumnWidthHandler extends AbstractColumnWidthStyleStrategy {


    @Override
    protected void setColumnWidth(WriteSheetHolder writeSheetHolder, List<WriteCellData<?>> cellDataList, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {
        if (Boolean.TRUE.equals(isHead)) {
            int columnWidth = cell.getStringCellValue().length();
            columnWidth = Math.max(columnWidth * 2, 20);
            if (columnWidth > 255) {
                columnWidth = 255;
            }
            writeSheetHolder.getSheet().setColumnWidth(cell.getColumnIndex(), columnWidth * 256);
        }
    }

}

2.4.22.4.32.4.4这三个拦截器类,主要是用于自定义导出Excel的一些样式,小伙伴也可以根据自己的需求,进行优化修改。

2.4.5 性别转换器

import com.alibaba.excel.converters.Converter;
import com.alibaba.excel.converters.ReadConverterContext;
import com.alibaba.excel.converters.WriteConverterContext;
import com.alibaba.excel.enums.CellDataTypeEnum;
import com.alibaba.excel.metadata.data.WriteCellData;
import com.test.java.base.GenderEnum;

/**
 * 性别转换器
 */
public class GenderConverter implements Converter<Integer> {

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

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

    // 读取Excel文件时将string转换为integer(导入)
    @Override
    public Integer convertToJavaData(ReadConverterContext<?> context) {
        return GenderEnum.convert(context.getReadCellData().getStringValue()).getValue();
    }

    // 写入Excel文件时将integer转换为string(导出)
    @Override
    public WriteCellData<?> convertToExcelData(WriteConverterContext<Integer> context) {
        return new WriteCellData<>(GenderEnum.convert(context.getValue()).getDescription());
    }

}

2.4.6 性别枚举

import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.AllArgsConstructor;
import lombok.Getter;

import java.util.stream.Stream;

/**
 * 性别枚举
 */
@Getter
@AllArgsConstructor
public enum GenderEnum {

    /**
     * 未知
     */
    UNKNOWN(0, "未知"),

    /**
     * 男性
     */
    MALE(1, "男性"),

    /**
     * 女性
     */
    FEMALE(2, "女性");

    private final Integer value;

    @JsonFormat
    private final String description;

    public static GenderEnum convert(Integer value) {
        return Stream.of(values())
                .filter(bean -> bean.value.equals(value))
                .findAny()
                .orElse(UNKNOWN);
    }

    public static GenderEnum convert(String description) {
        return Stream.of(values())
                .filter(bean -> bean.description.equals(description))
                .findAny()
                .orElse(UNKNOWN);
    }
}

2.5 结果

导出全部:

导出指定列:

三、EasyExcel常用注解

1、@ExcelProperty

必要的一个字段注解,注解中有三个参数value,index,converter分别代表列名,列序号,数据转换方式,通常不用设置converter
1.value 通过标题文本对应
2.index 通过文本行号对应
3.converter 转换器,通常入库和出库转换使用,如性别入库0和1,出库男和女

示例

public class StudentVo {

    /*** 姓名*/
    @ExcelProperty(value = "姓名", index = 1)
    private String studentName;

    /*** 手机号*/
    @ExcelProperty(value = "手机号", index = 2)
    private String phone;

    /*** 性别(1男 2女)*/
    @ExcelProperty(value = "性别", index = 3,converter = GenderConverter.class)
    private Integer gender;

}

2、@ColumnWith

设置列宽度,只有一个参数value,value的单位是字符长度,最大可以设置255个字符,因为一个excel单元格最大可以写入的字符个数就是255个字符。

示例

public class StudentVo implements Serializable {

    /*** 姓名*/
    @ExcelProperty(value = "姓名")
    @ColumnWidth(10)
    private String studentName;

}

3、@ContentFontStyle

用于设置单元格内容字体格式的注解。

参数 含义
fontName 字体名称
fontHeightInPoints 字体高度
italic 是否斜体
strikeout 是否设置删除水平线
color 字体颜色
typeOffset 偏移量
underline 下划线
bold 是否加粗
charset 编码格式

4、@ContentLoopMerge和@OnceAbsoluteMerge

相同点:这两个都是用于设置合并单元格的注解;它们都是按指定行数和指定列数去合并,并不能实现内容相同的合并。

不同点:@ContentLoopMerge是标注在字段上;@OnceAbsoluteMerge是标注在类上。

4.1 @ContentLoopMerge

参数 含义
eachRow 指定行数
columnExtend 指定列数

示例

@Data
public class Demo implements Serializable {

    @ExcelProperty(value = "商户名称", index = 0)
    private String appName;

    @ExcelProperty(value = "城市名称", index = 1)
    @ContentLoopMerge(eachRow = 2, columnExtend = 3)
    private String cityName;

    @ExcelProperty(value = "区域名称", index = 2)
    private String regionName;

    @ExcelProperty(value = "商圈名称", index = 3)
    private String businessAreaName;

    @ExcelProperty(value = "楼盘名称", index = 4)
    private String gardenName;

    @ExcelProperty(value = "楼栋名称", index = 5)
    private String buildingName;

    @ExcelProperty(value = "单元名称", index = 6)
    private String unitName;

    @ExcelProperty(value = "价格", index = 7)
    private Integer price;

}

结果: 

4.2 @OnceAbsoluteMerge

参数 含义
firstRowIndex 指定合并的第一行索引
lastRowIndex 指定合并的最后一行索引
firstColumnIndex 指定合并的第一列索引

示例

@Data
@OnceAbsoluteMerge(firstRowIndex = 1, lastRowIndex = 3 , firstColumnIndex = 1 , lastColumnIndex = 3)
public class Demo implements Serializable {

    @ExcelProperty(value = "商户名称", index = 0)
    private String appName;

    @ExcelProperty(value = "城市名称", index = 1)
    private String cityName;

    @ExcelProperty(value = "区域名称", index = 2)
    private String regionName;

    @ExcelProperty(value = "商圈名称", index = 3)
    private String businessAreaName;

    @ExcelProperty(value = "楼盘名称", index = 4)
    private String gardenName;

    @ExcelProperty(value = "楼栋名称", index = 5)
    private String buildingName;

    @ExcelProperty(value = "单元名称", index = 6)
    private String unitName;

    @ExcelProperty(value = "价格", index = 7)
    private Integer price;

}

 结果: 

5、@ContentRowHeight

用于设置行高。

参数 含义
value 行高,-1代表自动行高

6、@ContentStyle

用于设置内容格式。

参数 含义
dataFormat 日期格式
hidden 设置单元格使用此样式隐藏
locked 设置单元格使用此样式锁定
quotePrefix 在单元格前面增加`符号,数字或公式将以字符串形式展示
horizontalAlignment 设置是否水平居中
wrapped 设置文本是否应换行。将此标志设置为true通过在多行上显示使单元格中的所有内容可见
verticalAlignment 设置是否垂直居中
rotation 设置单元格中文本旋转角度。03版本的Excel旋转角度区间为-90°90°,07版本的Excel旋转角度区间为0°180°
indent 设置单元格中缩进文本的空格数
borderLeft 设置左边框的样式
borderRight 设置右边框样式
borderTop 设置上边框样式
borderBottom 设置下边框样式
leftBorderColor 设置左边框颜色
rightBorderColor 设置右边框颜色
topBorderColor 设置上边框颜色
bottomBorderColor 设置下边框颜色
fillPatternType 设置填充类型
fillBackgroundColor 设置背景色
fillForegroundColor 设置前景色
shrinkToFit 设置自动单元格自动大小

7、@HeadFontStyle

用于定制标题字体格式。

参数 含义
fontName 设置字体名称
fontHeightInPoints 设置字体高度
italic 设置字体是否斜体
strikeout 是否设置删除线
color 设置字体颜色
typeOffset 设置偏移量
underline 设置下划线
charset 设置字体编码
bold 设置字体是否加粗

8、@HeadRowHeight

用于设置标题行行高。

参数 含义
value 设置行高,-1代表自动行高

9、@HeadStyle

用于设置标题样式。

参数 含义
dataFormat 日期格式
hidden 设置单元格使用此样式隐藏
locked 设置单元格使用此样式锁定
quotePrefix 在单元格前面增加`符号,数字或公式将以字符串形式展示
horizontalAlignment 设置是否水平居中
wrapped 设置文本是否应换行。将此标志设置为true通过在多行上显示使单元格中的所有内容可见
verticalAlignment 设置是否垂直居中
rotation 设置单元格中文本旋转角度。03版本的Excel旋转角度区间为-90°90°,07版本的Excel旋转角度区间为0°180°
indent 设置单元格中缩进文本的空格数
borderLeft 设置左边框的样式
borderRight 设置右边框样式
borderTop 设置上边框样式
borderBottom 设置下边框样式
leftBorderColor 设置左边框颜色
rightBorderColor 设置右边框颜色
topBorderColor 设置上边框颜色
bottomBorderColor 设置下边框颜色
fillPatternType 设置填充类型
fillBackgroundColor 设置背景色
fillForegroundColor 设置前景色
shrinkToFit 设置自动单元格自动大小

10、@ExcelIgnore

不将该字段转换成Excel。

11、@ExcelIgnoreUnannotated

没有添加@ExcelProperty注解的字段都不转换。

12、@NumberFormat

用于数字格式化。

参数 示例 结果
value

@NumberFormat(value = "###.#")

66.8
@NumberFormat(value = "#.##%") 66.8%
@NumberFormat(value = "0.00") 66.80

参数value用#.##0.00的区别就是,比如导入的数值是66.80,#.##会忽略掉后面的0变成66.8,而0.00则不会忽略,保持原来的66.80,我这里用的是导入时保留2位小数的做法,关于@NumberFormat里面value的参数可以参照java.text.DecimalFormat

13、@DateTimeFormat

用于时间格式化。

参数 示例 结果
value @DateTimeFormat(value="yyyy-MM-dd") 2023-07-24

如果这篇文章对您有所帮助,或者有所启发的话,求一键三连:点赞、评论、收藏➕关注,您的支持是我坚持写作最大的动力。

猜你喜欢

转载自blog.csdn.net/weixin_42555014/article/details/131853805