EasyExcel簡介及使用方法

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/easyexceljava

EasyExcel優點

  • EasyExcel 最大的特色就是使用內存少,固然如今它的功能還比較簡單,可以面對的複雜場景比較少,不過基本的讀寫徹底能夠知足。
  • 註解式自定義操做。
  • 輸入輸出簡單,提供輸入輸出過程的接口
  • 支持必定程度的單元格合併等靈活化操做

EasyExcel劣勢

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

    首先是添加該項目的依賴,目前的版本是 1.0.2git

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

    二. 須要的類

    1. ExcelUtil

    工具類,能夠直接調用該工具類的方法完成 Excel 的讀或者寫github

     

    /**
     * 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 裏面。api

    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

    捕獲相關 Exceptionapp

     

    /**
     * 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 類,如:ide

    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 名以及對應的映射類

相關文章
相關標籤/搜索