Java导出Excel文件合集(easyExcel)

目录

前言

一、EasyExcel简介

1、网站

二、EasyExcel使用

1、相关依赖

2、实现代码

2.1 实体类

2.2 服务层

2.2.1 服务接口类

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

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

  • 6
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Javaの甘乃迪

感谢看客老爷的打赏

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值