EasyExcel使用教程

一、EasyExcel简介

1.EasyExcel介绍:EasyExcel是阿里巴巴开源的一个excel处理框架。
2.特性:使用简单、节省内存、适合处理大数据量的Excel。
数据解析时,不像之前的Apache poi、jxl等Excel解析框架将数据一次性全部加载到内存中,而是从磁盘上一行行读取数据,逐个解析,并将一行的解析结果以观察者的模式通知处理(AnalysisEventListener)。EasyExcel重写了poi对07版本Excel的解析,在数据量大时不容易内存溢出。
3.使用场景
(1)数据导入:减轻录入工作量
(2)数据导出:统计信息归档
(3)数据传输:异构系统之间数据传输
4.官方文档:https://alibaba-easyexcel.github.io/index.html

二、EasyExcel使用介绍

这里我也是参照官方文档做的,但也踩过坑,我这里介绍的会比官方文档更细致一些。我这里重点关注写的使用。
1.首先就是需要一个SpringBoot的项目,引入依赖,我这里用的最新的版本,注意如果有poi的依赖,会有冲突,需要移除掉。

 <!-- easyexcel依赖 -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>easyexcel</artifactId>
            <version>3.0.5</version>
        </dependency>
        

2.实现多sheet的写功能
注意这里不要和web的写弄混淆了。这里的往指定文件写入数据。
(1)创建一个Controller,这里需要一个实体类,一个程序入口,为了方便我就写在一起了。数据方面使用官网给的默认数据。有区别的是,我没使用lombok的@Data注释。新增的sheetName字段,不然按官方的例子全写到一个sheet页了。
(2)创建文件demoData.xlsx,并放入指定目录里面。

package com.example.springb_web.easyexcel;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.annotation.ExcelIgnore;
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.write.metadata.WriteSheet;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletResponse;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

@RestController
public class EasyExcelController {


    private String templatePath = "D:/Tools/easyExcelTemplate/";

    @RequestMapping(value="/exportTest")
    public void export_multi_sheet(HttpServletResponse response){
        String template = templatePath+"demoData.xlsx";
        // 指定模板文件
        ExcelWriter excelWriter = EasyExcel.write(template, DemoData.class).build();
        for (int i = 0; i < 5; i++) {
            // 每次都要创建writeSheet 这里注意必须指定sheetNo和sheetName
            String sheetName = "模板"+(i+1);
            WriteSheet writeSheet = EasyExcel.writerSheet(i, sheetName).build();
            // 分页去数据库查询数据 这里可以去数据库查询每一页的数据
            List<DemoData> data = data();
            excelWriter.write(data, writeSheet);
        }
        // 千万别忘记finish 会帮忙关闭流
        excelWriter.finish();
    }


    private List<DemoData> data() {
        List<DemoData> list = new ArrayList<DemoData>();
        for (int i = 0; i < 10; i++) {
            DemoData data = new DemoData();
            data.setString("字符串" + i);
            data.setDate(new Date());
            data.setDoubleData(0.56);
            list.add(data);
        }
        return list;
    }
}


class DemoData {
    @ExcelProperty("字符串标题")
    private String string;
    @ExcelProperty("日期标题")
    private Date date;
    @ExcelProperty("数字标题")
    private Double doubleData;
    /**
     * 忽略这个字段
     */
    @ExcelIgnore
    private String ignore;

    public String getString() {
        return string;
    }

    public void setString(String string) {
        this.string = string;
    }

    public Date getDate() {
        return date;
    }

    public void setDate(Date date) {
        this.date = date;
    }

    public Double getDoubleData() {
        return doubleData;
    }

    public void setDoubleData(Double doubleData) {
        this.doubleData = doubleData;
    }

    public String getIgnore() {
        return ignore;
    }

    public void setIgnore(String ignore) {
        this.ignore = ignore;
    }
}

(3)验证,进入http://localhost:8080/exportTest,在查看demoData.xlsx,结果如下。
在这里插入图片描述

3.实现web的写功能
直接使用官方的方法,我测试过没有问题,需要引入fastJson的依赖

 <!--fastJson-->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.28</version>
</dependency>
@GetMapping("downloadFailedUsingJson")
    public void downloadFailedUsingJson(HttpServletResponse response) throws IOException {
        // 这里注意 有同学反应使用swagger 会导致各种问题,请直接用浏览器或者用postman
        try {
            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(), DemoData.class).autoCloseStream(Boolean.FALSE).sheet("模板")
                    .doWrite(data());
        } catch (Exception e) {
            // 重置response
            response.reset();
            response.setContentType("application/json");
            response.setCharacterEncoding("utf-8");
            Map<String, String> map = new HashMap<String, String>();
            map.put("status", "failure");
            map.put("message", "下载文件失败" + e.getMessage());
            response.getWriter().println(JSON.toJSONString(map));
        }
    }

4.实现web的多sheet写功能
(1)完整代码如下,新增实现方法webMultiSheetWrite、增加标题的getHead和切割list的方法subListUtil。

package com.example.springb_web.easyexcel;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.annotation.ExcelIgnore;
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.alibaba.fastjson.JSON;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.net.URLEncoder;
import java.util.*;

@RestController
public class EasyExcelController {


    private String templatePath = "D:/Tools/easyExcelTemplate/";

    @RequestMapping(value="/exportTest")
    public void export_multi_sheet(HttpServletResponse response){
        String template = templatePath+"demoData.xlsx";
        // 指定模板文件
        ExcelWriter excelWriter = EasyExcel.write(template, DemoData.class).build();
        for (int i = 0; i < 5; i++) {
            // 每次都要创建writeSheet 这里注意必须指定sheetNo和sheetName
            String sheetName = "模板"+(i+1);
            WriteSheet writeSheet = EasyExcel.writerSheet(i, sheetName).build();
            // 分页去数据库查询数据 这里可以去数据库查询每一页的数据
            List<DemoData> data = data();
            excelWriter.write(data, writeSheet);
        }
        // 千万别忘记finish 会帮忙关闭流
        excelWriter.finish();
    }

    @GetMapping("downloadFailedUsingJson")
    public void downloadFailedUsingJson(HttpServletResponse response) throws IOException {
        // 这里注意 有同学反应使用swagger 会导致各种问题,请直接用浏览器或者用postman
        try {
            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(), DemoData.class).autoCloseStream(Boolean.FALSE).sheet("模板")
                    .doWrite(data());
        } catch (Exception e) {
            // 重置response
            response.reset();
            response.setContentType("application/json");
            response.setCharacterEncoding("utf-8");
            Map<String, String> map = new HashMap<String, String>();
            map.put("status", "failure");
            map.put("message", "下载文件失败" + e.getMessage());
            response.getWriter().println(JSON.toJSONString(map));
        }
    }

    @GetMapping("webMultiSheetWrite")
    public void webMultiSheetWrite(HttpServletResponse response) throws IOException {
        // 这里注意 有同学反应使用swagger 会导致各种问题,请直接用浏览器或者用postman
        try {
            response.setContentType("application/vnd.ms-excel");
            response.setCharacterEncoding("utf-8");
            // 这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系
            String fileName = URLEncoder.encode("多sheet写测试", "UTF-8");
            response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx");
            //设置每个sheet存3条数据
            int n = 3;
            //将数据拆分成多个sheet页
            List<List<DemoData>> userlistBysheet = subListUtil(data(),n);
            ExcelWriter excelWriter = EasyExcel.write(response.getOutputStream()).head(getHead("大标题")).build();
            for (int i = 0; i < userlistBysheet.size(); i++) {
                // 每次都要创建writeSheet 这里注意必须指定sheetNo
                String sheetName = "模板"+(i+1);
                WriteSheet writeSheet = EasyExcel.writerSheet(i, sheetName).build();
                List<DemoData> data = userlistBysheet.get(i);
                excelWriter.write(data, writeSheet);
            }
            excelWriter.finish();
        } catch (Exception e) {
            // 重置response
            response.reset();
            response.setContentType("application/json");
            response.setCharacterEncoding("utf-8");
            Map<String, String> map = new HashMap<String, String>();
            map.put("status", "failure");
            map.put("message", "下载文件失败" + e.getMessage());
            response.getWriter().println(JSON.toJSONString(map));
        }
    }

    private  List<List<String>> getHead(String bigTitle){
        List<List<String>> head = new ArrayList<List<String>>();
        List<String> head0 = new ArrayList<>();
        head0.add(bigTitle);
        head0.add("字符串标题");
        List<String> head1 = new ArrayList<>();
        head1.add(bigTitle);
        head1.add("日期标题");
        List<String> head2 = new ArrayList<>();
        head2.add(bigTitle);
        head2.add("数字标题");
        head.add(head0);
        head.add(head1);
        head.add(head2);
        return head;
    }

    //切割list方法
    public  <T> List<List<T>> subListUtil(List<T> source, int n) {
        List<List<T>> result = new ArrayList<>();
        //(先计算出余数)
        int remainder = source.size() % n;
        //然后是商
        int number = source.size() / n;
        //偏移量
        int offset = 0;
        List<T> value;
        for (int i = 0; i < number; i++) {
            value = source.subList(offset, offset + n);
            offset += n ;
            result.add(value);
        }
        value = source.subList(offset, offset + remainder);
        result.add(value);
        return result;
    }


    private List<DemoData> data() {
        List<DemoData> list = new ArrayList<DemoData>();
        for (int i = 0; i < 10; i++) {
            DemoData data = new DemoData();
            data.setString("字符串" + i);
            data.setDate(new Date());
            data.setDoubleData(0.56);
            list.add(data);
        }
        return list;
    }
}


class DemoData {
    @ExcelProperty("字符串标题")
    private String string;
    @ExcelProperty("日期标题")
    private Date date;
    @ExcelProperty("数字标题")
    private Double doubleData;
    /**
     * 忽略这个字段
     */
    @ExcelIgnore
    private String ignore;

    public String getString() {
        return string;
    }

    public void setString(String string) {
        this.string = string;
    }

    public Date getDate() {
        return date;
    }

    public void setDate(Date date) {
        this.date = date;
    }

    public Double getDoubleData() {
        return doubleData;
    }

    public void setDoubleData(Double doubleData) {
        this.doubleData = doubleData;
    }

    public String getIgnore() {
        return ignore;
    }

    public void setIgnore(String ignore) {
        this.ignore = ignore;
    }
}


(2)测试
进入http://localhost:8080/webMultiSheetWrite,可以看到十条数据,每三条一个sheet页,被正确拆分了。
在这里插入图片描述

三、EasyExcel常用注解介绍

1.@ExcelProperty

@ExcelProperty("标题")
@ExcelProperty(value="标题",index=0)

value用来自定义表头(二级表头只需要使用 {一级表头,二级表头} 来实现),index用来选择excel中的顺序(第一排为0)
2.@ExcelIgnore
在实际应用中,修改存量的程序,对象中不需要导出的多余的属性,需要使用@ExcelIgnore注解,否则会导出很多你不需要的字段。
4.@DateTimeFormat
日期格式转换
4.@ColumnWidth
设置此属性的行宽

四、EasyExcel常见报错解决

1.ExcelDataConverException:Cannot find ‘Converter’ support class
比如提示你日期格式无法导出
解决方案:
在需要转换字段较少的情况下使用方案1较为简便,长久考虑还是方案2比较好。
(1)在对象中新增一个字段,以字符串存储,修改如下:

@ExcelProperty
private Date date;

改成这个,然后在list中转行给date_str赋值

@ExcelIgnore
private Date date;
@ExcelProperty
private String date_str;

(2)创建一个类,实现 Converter<> 接口,泛型为需要转换的类型 ,并重写其中的方法。

public class LocalDateTimeConverter implements Converter<LocalDateTime> {

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

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

    //将格式化后的日期转换为字符串
    @Override
    public LocalDateTime convertToJavaData(CellData cellData, ExcelContentProperty contentProperty,
                                           GlobalConfiguration globalConfiguration) {
        return LocalDateTime.parse(cellData.getStringValue(), DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
    }

    //将LocalDateTime类型格式化
    @Override
    public CellData<String> convertToExcelData(LocalDateTime value, ExcelContentProperty contentProperty,
                                               GlobalConfiguration globalConfiguration) {
        return new CellData<>(value.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
    }
}

然后在@ExcelProperty标签下增加该Converter类

 @ExcelProperty(
            converter = LocalDateTimeConverter.class
    )
  • 10
    点赞
  • 47
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值