EasyExcel简介及使用方法

原创
2019/11/27 22:39
阅读数 1.3K

EasyExcel简介

  • easyExcel是阿里巴巴开源poi插件之一,当前最新版本1.1.2-beta5,poi版本3.17,因此,集成时老版本poi需要提升poi版本,或者做版本隔离。
  • 吐槽一下这个版本没有RELEASE版本
  • 主要解决了poi框架使用复杂,sax解析模式不容易操作,数据量大起来容易OOM,解决了POI并发造成的报错
  • 主要解决方式:通过解压文件的方式加载,一行一行的加载,并且抛弃样式字体等不重要的数据,降低内存的占用
  • 具体实现原理,建议看github上的readme

 

EasyExcel 的 github 地址: https://github.com/alibaba/easyexcel

EasyExcel优势

  • EasyExcel 最大的特点就是使用内存少,当然现在它的功能还比较简单,能够面对的复杂场景比较少,不过基本的读写完全可以满足。
  • 注解式自定义操作。
  • 输入输出简单,提供输入输出过程的接口
  • 支持一定程度的单元格合并等灵活化操作

EasyExcel劣势

  • 框架不成熟,1.1.0版本后提供灵活接口的只剩beta版本
  • 依然存在一些bug
  • 没有一套完整的api
  • 一. 依赖

    首先是添加该项目的依赖,目前的版本是 1.0.2

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

    二. 需要的类

    1. ExcelUtil

    工具类,可以直接调用该工具类的方法完成 Excel 的读或者写

     

    /**
     * Created with IntelliJ IDEA
     *
     * @Author yuanhaoyue swithaoy@gmail.com
     * @Description 工具类
     * @Date 2018-06-06
     * @Time 14:07
     */
    public class ExcelUtil {
        /**
         * 读取 Excel(多个 sheet)
         *
         * @param excel    文件
         * @param rowModel 实体类映射,继承 BaseRowModel 类
         * @return Excel 数据 list
         */
        public static List<Object> readExcel(MultipartFile excel, BaseRowModel rowModel) {
            ExcelListener excelListener = new ExcelListener();
            ExcelReader reader = getReader(excel, excelListener);
            if (reader == null) {
                return null;
            }
            for (Sheet sheet : reader.getSheets()) {
                if (rowModel != null) {
                    sheet.setClazz(rowModel.getClass());
                }
                reader.read(sheet);
            }
            return excelListener.getDatas();
        }
    
        /**
         * 读取某个 sheet 的 Excel
         *
         * @param excel    文件
         * @param rowModel 实体类映射,继承 BaseRowModel 类
         * @param sheetNo  sheet 的序号 从1开始
         * @return Excel 数据 list
         */
        public static List<Object> readExcel(MultipartFile excel, BaseRowModel rowModel, int sheetNo) {
            return readExcel(excel, rowModel, sheetNo, 1);
        }
    
        /**
         * 读取某个 sheet 的 Excel
         *
         * @param excel       文件
         * @param rowModel    实体类映射,继承 BaseRowModel 类
         * @param sheetNo     sheet 的序号 从1开始
         * @param headLineNum 表头行数,默认为1
         * @return Excel 数据 list
         */
        public static List<Object> readExcel(MultipartFile excel, BaseRowModel rowModel, int sheetNo,
                                             int headLineNum) {
            ExcelListener excelListener = new ExcelListener();
            ExcelReader reader = getReader(excel, excelListener);
            if (reader == null) {
                return null;
            }
            reader.read(new Sheet(sheetNo, headLineNum, rowModel.getClass()));
            return excelListener.getDatas();
        }
    
        /**
         * 导出 Excel :一个 sheet,带表头
         *
         * @param response  HttpServletResponse
         * @param list      数据 list,每个元素为一个 BaseRowModel
         * @param fileName  导出的文件名
         * @param sheetName 导入文件的 sheet 名
         * @param object    映射实体类,Excel 模型
         */
        public static void writeExcel(HttpServletResponse response, List<? extends BaseRowModel> list,
                                      String fileName, String sheetName, BaseRowModel object) {
            ExcelWriter writer = new ExcelWriter(getOutputStream(fileName, response), ExcelTypeEnum.XLSX);
            Sheet sheet = new Sheet(1, 0, object.getClass());
            sheet.setSheetName(sheetName);
            writer.write(list, sheet);
            writer.finish();
        }
    
        /**
         * 导出 Excel :多个 sheet,带表头
         *
         * @param response  HttpServletResponse
         * @param list      数据 list,每个元素为一个 BaseRowModel
         * @param fileName  导出的文件名
         * @param sheetName 导入文件的 sheet 名
         * @param object    映射实体类,Excel 模型
         */
        public static ExcelWriterFactroy writeExcelWithSheets(HttpServletResponse response, List<? extends BaseRowModel> list,
                                                              String fileName, String sheetName, BaseRowModel object) {
            ExcelWriterFactroy writer = new ExcelWriterFactroy(getOutputStream(fileName, response), ExcelTypeEnum.XLSX);
            Sheet sheet = new Sheet(1, 0, object.getClass());
            sheet.setSheetName(sheetName);
            writer.write(list, sheet);
            return writer;
        }
    
        /**
         * 导出文件时为Writer生成OutputStream
         */
        private static OutputStream getOutputStream(String fileName, HttpServletResponse response) {
            //创建本地文件
            String filePath = fileName + ".xlsx";
            File dbfFile = new File(filePath);
            try {
                if (!dbfFile.exists() || dbfFile.isDirectory()) {
                    dbfFile.createNewFile();
                }
                fileName = new String(filePath.getBytes(), "ISO-8859-1");
                response.addHeader("Content-Disposition", "filename=" + fileName);
                return response.getOutputStream();
            } catch (IOException e) {
                throw new ExcelException("创建文件失败!");
            }
        }
    
        /**
         * 返回 ExcelReader
         *
         * @param excel         需要解析的 Excel 文件
         * @param excelListener new ExcelListener()
         */
        private static ExcelReader getReader(MultipartFile excel,
                                             ExcelListener excelListener) {
            String filename = excel.getOriginalFilename();
            if (filename == null || (!filename.toLowerCase().endsWith(".xls") && !filename.toLowerCase().endsWith(".xlsx"))) {
                throw new ExcelException("文件格式错误!");
            }
            InputStream inputStream;
            try {
                inputStream = new BufferedInputStream(excel.getInputStream());
                return new ExcelReader(inputStream, null, excelListener, false);
            } catch (IOException e) {
                e.printStackTrace();
            }
            return null;
        }
    }
    

     

    2. ExcelListener

    监听类,可以根据需要与自己的情况,自定义处理获取到的数据,我这里只是简单地把数据添加到一个 List 里面。

    public class ExcelListener extends AnalysisEventListener {
    
        //自定义用于暂时存储data。
        //可以通过实例获取该值
        private List<Object> datas = new ArrayList<>();
    
        /**
         * 通过 AnalysisContext 对象还可以获取当前 sheet,当前行等数据
         */
        @Override
        public void invoke(Object object, AnalysisContext context) {
            //数据存储到list,供批量处理,或后续自己业务逻辑处理。
            datas.add(object);
            //根据自己业务做处理
            doSomething(object);
        }
    
        private void doSomething(Object object) {
        }
    
        @Override
        public void doAfterAllAnalysed(AnalysisContext context) {
            /*
                datas.clear();
                解析结束销毁不用的资源
             */
        }
    
        public List<Object> getDatas() {
            return datas;
        }
    
        public void setDatas(List<Object> datas) {
            this.datas = datas;
        }
    }
    

    3. ExcelWriterFactroy

    用于导出多个 sheet 的 Excel,通过多次调用 write 方法写入多个 sheet

    /**
     * Created with IntelliJ IDEA
     *
     * @Author yuanhaoyue swithaoy@gmail.com
     * @Description
     * @Date 2018-06-07
     * @Time 16:47
     */
    public class ExcelWriterFactroy extends ExcelWriter {
        private OutputStream outputStream;
        private int sheetNo = 1;
    
        public ExcelWriterFactroy(OutputStream outputStream, ExcelTypeEnum typeEnum) {
            super(outputStream, typeEnum);
            this.outputStream = outputStream;
        }
    
        public ExcelWriterFactroy write(List<? extends BaseRowModel> list, String sheetName,
                                        BaseRowModel object) {
            this.sheetNo++;
            try {
                Sheet sheet = new Sheet(sheetNo, 0, object.getClass());
                sheet.setSheetName(sheetName);
                this.write(list, sheet);
            } catch (Exception ex) {
                ex.printStackTrace();
                try {
                    outputStream.flush();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            return this;
        }
    
        @Override
        public void finish() {
            super.finish();
            try {
                outputStream.flush();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    

     

    4. ExcelException

    捕获相关 Exception

     

    /**
     * Created with IntelliJ IDEA
     *
     * @Author yuanhaoyue swithaoy@gmail.com
     * @Description Excel 解析 Exception
     * @Date 2018-06-06
     * @Time 15:56
     */
    public class ExcelException extends RuntimeException {
        public ExcelException(String message) {
            super(message);
        }
    }
    

     

    三. 读取 Excel

    读取 Excel 时只需要调用 ExcelUtil.readExcel() 方法

    @RequestMapping(value = "readExcel", method = RequestMethod.POST)
    public Object readExcel(MultipartFile excel) {
        return ExcelUtil.readExcel(excel, new ImportInfo());
    }
    

    其中 excel 是 MultipartFile 类型的文件对象,而 new ImportInfo() 是该 Excel 所映射的实体对象,需要继承 BaseRowModel 类,如:

    public class ImportInfo extends BaseRowModel {
        @ExcelProperty(index = 0)
        private String name;
    
        @ExcelProperty(index = 1)
        private String age;
    
        @ExcelProperty(index = 2)
        private String email;
    
        /*
            作为 excel 的模型映射,需要 setter 方法
         */
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public String getAge() {
            return age;
        }
    
        public void setAge(String age) {
            this.age = age;
        }
    
        public String getEmail() {
            return email;
        }
    
        public void setEmail(String email) {
            this.email = email;
        }
    }
    

    作为映射实体类,通过 @ExcelProperty 注解与 index 变量可以标注成员变量所映射的列,同时不可缺少 setter 方法

    四. 导出 Excel

    1. 导出的 Excel 只拥有一个 sheet

    只需要调用 ExcelUtil.writeExcelWithSheets() 方法:

    @RequestMapping(value = "writeExcel", method = RequestMethod.GET)
    public void writeExcel(HttpServletResponse response) throws IOException {
        List<ExportInfo> list = getList();
        String fileName = "一个 Excel 文件";
        String sheetName = "第一个 sheet";
    
        ExcelUtil.writeExcel(response, list, fileName, sheetName, new ExportInfo());
        }
    

    fileName,sheetName 分别是导出文件的文件名和 sheet 名,new ExportInfo() 为导出数据的映射实体对象,list 为导出数据。

    对于映射实体类,可以根据需要通过 @ExcelProperty 注解自定义表头,当然同样需要继承 BaseRowModel 类,如:

    public class ExportInfo extends BaseRowModel {
        @ExcelProperty(value = "姓名" ,index = 0)
        private String name;
    
        @ExcelProperty(value = "年龄",index = 1)
        private String age;
    
        @ExcelProperty(value = "邮箱",index = 2)
        private String email;
    
        @ExcelProperty(value = "地址",index = 3)
        private String address;
    }
    

    value 为列名,index 为列的序号

    如果需要复杂一点,可以实现如下图的效果: 

    对应的实体类写法如下:

    public class MultiLineHeadExcelModel extends BaseRowModel {
    
        @ExcelProperty(value = {"表头1","表头1","表头31"},index = 0)
        private String p1;
    
        @ExcelProperty(value = {"表头1","表头1","表头32"},index = 1)
        private String p2;
    
        @ExcelProperty(value = {"表头3","表头3","表头3"},index = 2)
        private int p3;
    
        @ExcelProperty(value = {"表头4","表头4","表头4"},index = 3)
        private long p4;
    
        @ExcelProperty(value = {"表头5","表头51","表头52"},index = 4)
        private String p5;
    
        @ExcelProperty(value = {"表头6","表头61","表头611"},index = 5)
        private String p6;
    
        @ExcelProperty(value = {"表头6","表头61","表头612"},index = 6)
        private String p7;
    
        @ExcelProperty(value = {"表头6","表头62","表头621"},index = 7)
        private String p8;
    
        @ExcelProperty(value = {"表头6","表头62","表头622"},index = 8)
        private String p9;
    }
    

    2. 导出的 Excel 拥有多个 sheet

    调用 ExcelUtil.writeExcelWithSheets() 处理第一个 sheet,之后调用 write() 方法依次处理之后的 sheet,最后使用 finish() 方法结束

    public void writeExcelWithSheets(HttpServletResponse response) throws IOException {
        List<ExportInfo> list = getList();
        String fileName = "一个 Excel 文件";
        String sheetName1 = "第一个 sheet";
        String sheetName2 = "第二个 sheet";
        String sheetName3 = "第三个 sheet";
    
        ExcelUtil.writeExcelWithSheets(response, list, fileName, sheetName1, new ExportInfo())
                    .write(list, sheetName2, new ExportInfo())
                    .write(list, sheetName3, new ExportInfo())
                    .finish();
    }
    

    write 方法的参数为当前 sheet 的 list 数据,当前 sheet 名以及对应的映射类

展开阅读全文
加载中

作者的其它热门文章

打赏
0
0 收藏
分享
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部