EasyExcel

  • 1 EasyExcel的集成
  • 1.1 引入依赖
  • 1.2 模型映射
  • 1.3 读Excel
  • 1.4 写Excel
  • 1.5 web上传、下载
  • 2 自定义多Sheet页下载
  • 2.1 工具类
  • 2.2 方法入参
  • 3 优点


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

1 EasyExcel的集成

1.1 引入依赖

<dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>easyexcel</artifactId>
            <version>2.0.5</version>
        </dependency>

1.2 模型映射

import com.alibaba.excel.annotation.ExcelProperty;
import lombok.Data;
import java.util.Date;

@Data
public class DemoData {

    /**
     * 编号
     */
    @ExcelProperty(index = 0)
    private String code;

    /**
     * 时间
     */
    @ExcelProperty(format = "yyyy/MM/dd",value = "时间")
    private Date reportTime;

    /**
     * 姓名
     */
    @ExcelProperty({"姓名"})
    private String name;

}

该实体相当于跟Excel解析出来的列一一对应,其中ExcelProperty注解的属性中index表示指定的列,注意是从0开始的,format是定义的格式,value是名称。

1.3 读Excel

/**
     * 最简单的读
     * <p>1. 创建excel对应的实体对象 参照{@link DemoData}
     * <p>2. 由于默认一行行的读取excel,所以需要创建excel一行一行的回调监听器,参照{@link DemoDataListener}
     * <p>3. 直接读即可
     */
    @Test
    public void simpleRead() {
        String fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx";
        // 这里 需要指定读用哪个class去读,然后读取第一个sheet 文件流会自动关闭
        EasyExcel.read(fileName, DemoData.class, new DemoDataListener()).sheet().doRead();
    }

1.4 写Excel

/**
     * 最简单的写
     * <p>1. 创建excel对应的实体对象 参照{@link com.alibaba.easyexcel.test.demo.write.DemoData}
     * <p>2. 直接写即可
     */
    @Test
    public void simpleWrite() {
        String fileName = TestFileUtil.getPath() + "write" + System.currentTimeMillis() + ".xlsx";
        // 这里 需要指定写用哪个class去读,然后写到第一个sheet,名字为模板 然后文件流会自动关闭
        // 如果这里想使用03 则 传入excelType参数即可
        EasyExcel.write(fileName, DemoData.class).sheet("模板").doWrite(data());
    }

1.5 web上传、下载

* 文件下载(失败了会返回一个有部分数据的Excel)
     * <p>1. 创建excel对应的实体对象 参照{@link DownloadData}
     * <p>2. 设置返回的 参数
     * <p>3. 直接写,这里注意,finish的时候会自动关闭OutputStream,当然你外面再关闭流问题不大
     */
    @GetMapping("download")
    public void download(HttpServletResponse response) throws IOException {
        // 这里注意 有同学反应使用swagger 会导致各种问题,请直接用浏览器或者用postman
        response.setContentType("application/vnd.ms-excel");
        response.setCharacterEncoding("utf-8");
        // 这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系
        String fileName = URLEncoder.encode("测试", "UTF-8");
        response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx");
        EasyExcel.write(response.getOutputStream(), DownloadData.class).sheet("模板").doWrite(data());
    }

    /**
     * 文件上传
     * <p>1. 创建excel对应的实体对象 参照{@link UploadData}
     * <p>2. 由于默认一行行的读取excel,所以需要创建excel一行一行的回调监听器,参照{@link UploadDataListener}
     * <p>3. 直接读即可
     */
    @PostMapping("upload")
    @ResponseBody
    public String upload(MultipartFile file) throws IOException {
        EasyExcel.read(file.getInputStream(), UploadData.class, new UploadDataListener(uploadDAO)).sheet().doRead();
        return "success";
    }

2 自定义多Sheet页下载

2.1 工具类

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.alibaba.excel.write.metadata.style.WriteCellStyle;
import com.alibaba.excel.write.metadata.style.WriteFont;
import com.alibaba.excel.write.style.HorizontalCellStyleStrategy;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.usermodel.BorderStyle;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.apache.poi.ss.usermodel.IndexedColors;
import org.apache.poi.ss.usermodel.VerticalAlignment;

import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;

/**
 * @Description 多sheet页下载
 * @Author zhangxu
 * @Date 2020/5/11 14:46
 **/
@Slf4j
public class EasyExcelUtils {
    /**
     * 下载文件类型
     */
    private static final String FILE_TYPE = ".xlsx";

    /**
     * 多sheet页导出
     * @param response
     * @param fileName
     * @param list
     * @param sheetNameList
     */
    public static void exportWebExcel(HttpServletResponse response, String fileName, List<List<?>> list, List<String> sheetNameList) {
        response.setContentType("application/vnd.ms-excel");
        response.setCharacterEncoding("utf-8");
        ExcelWriter excelWriter = null;
        try {
            // 这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系
            fileName = URLEncoder.encode(fileName, "UTF-8");
            response.setHeader("Content-disposition", "attachment;filename=" + fileName + FILE_TYPE);
            excelWriter = EasyExcel.write(response.getOutputStream()).registerWriteHandler(EasyExcelUtils.setHorizontalCellStyleStrategy()).build();
            //这里 需要指定写用哪个class去写
            for (int i = 0; i <= list.size() - 1; i++) {
                WriteSheet writeSheet;
                for (int j = 0; j <= list.get(i).size() - 1; j++) {
                    List<Object> list0 = new ArrayList<>();
                    list0.add(list.get(i).get(j));
                    writeSheet = EasyExcel.writerSheet(i, sheetNameList.get(i)).head(list.get(i).get(0).getClass()).build();
                    excelWriter.write(list0, writeSheet);
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            excelWriter.finish();
        }
    }

    /**
     * 设置样式
     *
     * @return
     */
    public static HorizontalCellStyleStrategy setHorizontalCellStyleStrategy() {
        /*******自定义列标题和内容的样式******/
        // 头的策略
        WriteCellStyle headWriteCellStyle = new WriteCellStyle();
        headWriteCellStyle.setFillForegroundColor(IndexedColors.WHITE.getIndex());
        WriteFont headWriteFont = new WriteFont();
        headWriteFont.setFontHeightInPoints((short) 11);
        headWriteFont.setBold(false);
        headWriteCellStyle.setWrapped(false);
        headWriteCellStyle.setWriteFont(headWriteFont);
        headWriteCellStyle.setBorderBottom(BorderStyle.THIN);
        // 内容的策略
        WriteCellStyle contentWriteCellStyle = new WriteCellStyle();
        // 背景色
        // contentWriteCellStyle.setFillForegroundColor(IndexedColors.GREEN.getIndex());
        WriteFont contentWriteFont = new WriteFont();
        // 字体大小
        contentWriteFont.setFontHeightInPoints((short) 11);
        contentWriteFont.setFontName("宋体");
        contentWriteCellStyle.setWriteFont(contentWriteFont);
        //设置 自动换行
        contentWriteCellStyle.setWrapped(false);
        //设置 垂直居中
        contentWriteCellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
        //设置 水平居中
        contentWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);
        // 这个策略是 头是头的样式 内容是内容的样式
        HorizontalCellStyleStrategy horizontalCellStyleStrategy = new HorizontalCellStyleStrategy(headWriteCellStyle, contentWriteCellStyle);
        return horizontalCellStyleStrategy;
    }

}

如果单个sheet页的下载满足不了你的需求,那么上边的多sheet页的导出可以供你参考,下边的方法是用来自定义样式的。其中该类上的@Slf4j是用来操作日志的,如果你不需要可以直接干掉。

2.2 方法入参

如果你有多sheet页下载的需求,可以直接拷贝该工具类就可以使用,当然,具体的接下来介绍一下该方法的入参:

  • HttpServletResponse response: 响应的response对 象
  • String fileName: 下载的文件的名称
  • List<List<?>> list : 这个list用到了无边界通配符,可以传入任意的实体,想要了解泛型的也可以看下之前的文章 java中的泛型(基础篇)
  • List sheetNameList: 每个sheet页的名称

3 优点

  • 读Excel自动通过注解,把结果映射为java模型
  • 读写Excel支持多sheet
  • 写任意大07版Excel不会OOM
  • 写Excel通过注解将表头自动写入Excel
  • 写Excel可以自定义Excel样式 如:字体,加粗,表头颜色,数据内容颜色
  • 写Excel时候自定义是否需要写表头