POI和EasyExcel讲解

POI和EasyExcel讲解,第1张

全文内容来自官方文档和相关视频的讲解,本作者用做笔记!

POI和EasyExcel讲解
  • 常用场景:
  • Apache POI
    • Apache POI 官网:
    • 基本功能:
    • Excel 版本区别:03版和07版
    • POI-Excel 写
      • 创建普通Maven项目:
      • 创建完项目,导入依赖 pom.xml:
      • Java编程,万物皆对象:
      • 实现类,写入
      • 大文件写入HSSF (07版)
      • 大文件写SXSSF (比07版,速度更快)
    • POI-Excel 读
      • 03版本,读数据:
      • 07版本,读数据:
      • 读取不同数据类型:(最麻烦)注意类型转换
      • 读取并计算公式:
  • EasyExcel
    • EasyExcel:快速、简洁、解决大文件内存溢出的java处理Excel工具
      • POI一般底层仍会使用,弊端就是当数据量大的时候会报OOM异常,相比较POI相对简单一些!
      • 官方文档:
      • pom.xml 依赖
      • 实体类:EasyExcel会根据实体类自动生成表
      • 实现类:写数据
      • 实现类:读数据
      • 持久层:DAO
      • DemoDataListener:

常用场景:
  1. 将用户信息导出为Excel表格(导出xls数据…)
  2. 将Excel表中的信息录入到网站数据库(习题上传…公司找老师填写题目到excel再上传网站,网站会利用相关POI将数据回填到数据库,这样大大减轻网站录入量。)

开发中经常会设计到excel的处理,如导出Excel,导入Excel到数据库中!
*** 作Excel目前流行的就是Apache POI 和 阿里巴巴 EasyExcel!


Apache POI Apache POI 官网:

Apache POI 官网:https://poi.apache.org/

  Apache POI 是Apache软件基金会的开放源码函数库,用Java编写的免费开源的跨平台的 Java API。Apache POI提供API给Java程序对Microsoft Office格式档案读和写的功能。POI为“Poor Obfuscation Implementation”的首字母缩写,意为“简洁版的模糊实现”。

基本功能:

HSSF - 提供读写Microsoft Excel XLS格式档案的功能。
XSSF - 提供读写Microsoft Excel OOXML XLSX格式档案的功能。
HWPF - 提供读写Microsoft Word DOC格式档案的功能。
HSLF - 提供读写Microsoft PowerPoint格式档案的功能。
HDGF - 提供读Microsoft Visio格式档案的功能。
HPBF - 提供读Microsoft Publisher格式档案的功能。
HSMF - 提供读Microsoft Outlook格式档案的功能。

Excel 版本区别:03版和07版

03版本 2003版本.xls:行数只有65535行,多了放不了。

07版本 2007版本.xlsx:无限制


POI-Excel 写 创建普通Maven项目:

Java版本:1.8

创建完项目,导入依赖 pom.xml:

    <dependencies>
      
        <dependency>
            <groupId>org.apache.poigroupId>
            <artifactId>poiartifactId>
            <version>3.9version>
        dependency>

      
        <dependency>-->
            <groupId>org.apache.poigroupId>
            <artifactId>poi-ooxmlartifactId>
            <version>3.9version>
        dependency>

        
        <dependency>
            <groupId>joda-timegroupId>
            <artifactId>joda-timeartifactId>
            <version>2.10.1version>
        dependency>

        
        <dependency>
            <groupId>junitgroupId>
            <artifactId>junitartifactId>
            <version>4.12version>
        dependency>
    dependencies>

Java编程,万物皆对象:

excel 表中的各个对象: 工作簿、工作表、行、列、单元格

实现类,写入

先创建工作簿,后创建工作表,再有行和列,最后组成了数据:

public class ExcelWriteTest { //03版本实现类

String PATH = "/Users/**/Documents/IdeaProjects/guo_poi/src/main/resources/data/" ;//设置固定文件路径

    @Test  //创建03版本
    public void testWrite03() throws Exception{
        // 1.创建一个工作簿
        Workbook workbook = new HSSFWorkbook();  //对象存在差别 HSSF
        // 2.创建一个工作表
        Sheet sheet = workbook.createSheet("guo1");  //sheet  名字为:guo1
        // 3.创建一个行 (1,1)
        Row row11 = sheet.createRow(0);  //第一行
        // 4. 创建一个单元格
        Cell cell11 = row11.createCell(0);  //第一行,第一列的格子(0,0)
        cell11.setCellValue("今日人数");  //设置值

        //(1,2)
        Row row12 = sheet.createRow(1);
        Cell cell12 = row12.createCell(0);
        cell12.setCellValue(666);

        // 第二行
        Row row2 = sheet.createRow(1);
        Cell cell21 = row2.createCell(0);
        cell21.setCellValue("统计时间");
        // (2,2)
        Cell cell22 = row2.createCell(1);
        String time = new DateTime().toString("yyyy-mm-dd HH:mm:ss");
        cell22.setCellValue(time);

        //生成一张表(IO流) 03版本 就是使用xls结尾!
        FileOutputStream fileOutputStream = new FileOutputStream(PATH + "03版本统计表.xls");

        //输出
        workbook.write(fileOutputStream);
        //关闭流
        fileOutputStream.close();

        System.out.println("03版本统计表.xls 生成完毕!");
    }
}
public class ExcelWriteTest { //07版本实现类 和03版本写在同一个类下
  //设置固定文件路径,与03相同
String PATH = "/Users/**/Documents/IdeaProjects/guo_poi/src/main/resources/data/" ;
    @Test //创建07版本
    public void testWrite07() throws Exception{

        // 1.创建一个工作簿
        Workbook workbook = new XSSFWorkbook();  //对象存在差别 XSSF
        // 2.创建一个工作表
        Sheet sheet = workbook.createSheet("guo2");  //sheet  名字为:guo1
        // 3.创建一个行 (1,1)
        Row row11 = sheet.createRow(0);  //第一行
        // 4. 创建一个单元格
        Cell cell11 = row11.createCell(0);  //第一行,第一列的格子(0,0)
        cell11.setCellValue("今日人数");  //设置值

        //(1,2)
        Row row12 = sheet.createRow(1);
        Cell cell12 = row12.createCell(0);
        cell12.setCellValue(666);

        // 第二行
        Row row2 = sheet.createRow(1);
        Cell cell21 = row2.createCell(0);
        cell21.setCellValue("统计时间");
        // (2,2)
        Cell cell22 = row2.createCell(1);
        String time = new DateTime().toString("yyyy-mm-dd HH:mm:ss");
        cell22.setCellValue(time);

        //生成一张表(IO流) 03版本 就是使用xlsx结尾!   //这里存在差别
        FileOutputStream fileOutputStream = new FileOutputStream(PATH + "07版本统计表.xlsx");

        //输出
        workbook.write(fileOutputStream);
        //关闭流
        fileOutputStream.close();

        System.out.println("07版本统计表.xlsx 生成完毕!")
    }
}

03与07差别只有文件后缀不同!

数据批量写入:大文件写入HSSF (03版)

缺点:最多只能处理65536行,否则会抛出异常

java.lang.11legalArgumentException : Invalid row number (65536) outside allowable range (0..65535)

优点:过程中写入缓存,不 *** 作磁盘,最后一次性写入磁盘,速度快。

 //设置固定文件路径,与03相同
String PATH = "/Users/**/Documents/IdeaProjects/guo_poi/src/main/resources/data/" ;
   
    @Test  //03大量数据
    public void testWrite03BigData() throws IOException {
        //时间差 begin 和 end 的差值
        long begin = System.currentTimeMillis();

        // 创建工作簿
        Workbook workbook = new HSSFWorkbook(); //03版本对象 HSSF
        // 创建工作表
        Sheet sheet = workbook.createSheet();
        // 写入数据
        for( int rowNum = 0; rowNum < 65536; rowNum++){ //65537会报错
            Row row = sheet.createRow(rowNum);    // 行
            for (int cellNum = 0; cellNum <10; cellNum++){  //这里设置了10列
                Cell cell = row.createCell(cellNum);   // 列
                cell.setCellValue(cellNum);  //循环65536行,每一行都是10列的数据
            }
        }
        //运行完毕,输出文件流
        System.out.println("over");
        FileOutputStream outputStream = new FileOutputStream(PATH + "testWrite03BigDATA.xls" );
        workbook.write(outputStream);  //写入
        // 关闭流
        outputStream.close();
        //时间差
        long end = System.currentTimeMillis();
        //计算时间差,强制转换double,按秒来
        System.out.println((double)(end - begin)/1000);
    }
大文件写入HSSF (07版)

缺点: 写数据时速度非常慢,非常耗内存,可能会发生内存溢出,如100万条。
优点,可以写入较大的数据量,如20万条数据。

//设置固定文件路径,与03相同
String PATH = "/Users/**/Documents/IdeaProjects/guo_poi/src/main/resources/data/" ;

    @Test //07大数据读取 耗时较长
    public void testWrite07BigData() throws IOException {
        //时间差 begin 和 end 的差值
        long begin = System.currentTimeMillis();

        // 创建工作簿
        Workbook workbook = new XSSFWorkbook(); //07版本对象 XSSF
        // 创建工作表
        Sheet sheet = workbook.createSheet();
        // 写入数据
        for( int rowNum = 0; rowNum < 1000000; rowNum++){
            Row row = sheet.createRow(rowNum);    // 行
            for (int cellNum = 0; cellNum <10; cellNum++){  //这里设置了10列
                Cell cell = row.createCell(cellNum);   // 列
                cell.setCellValue(cellNum);  //循环65536行,每一行都是10列的数据
            }
        }
        //运行完毕,输出文件流
        System.out.println("over");
        FileOutputStream outputStream = new FileOutputStream(PATH + "testWrite07BigDATA.xlsx" );
        workbook.write(outputStream);  //写入
        // 关闭流
        outputStream.close();
        //时间差
        long end = System.currentTimeMillis();
        //计算时间差,强制转换double,按秒来
        System.out.println((double)(end - begin)/1000);
    }
大文件写SXSSF (比07版,速度更快)

优点:可以写非常大的数据量,如100万条甚至更多条,写数据速度快,占用更少的内存。
注意:过程中会产生临时文件,需要清理临时文件
原理:默认由100条记录保存在内存中,如果超出这数量,则最前面的数据最前面的数据被写入临时文件,如果想自定义内存中的数据的数量,可以使用new SXSSFWorkbook(数量)
SXSSFWorkbook 来至官方的解释,实现“BigGridDemo“策略的流式XSSFWorkbook版本,这允许写入非常大的文件而不会耗尽内存,因为任何时候只有可配置的行部分被保存在内存中。
请注意,仍然可能会消耗大量内存,这些内存基于您正在使用的功能,例如合并区域,注释,仍然只能存储在内存中,因此如果广泛使用,可能需要大量内存。

//设置固定文件路径,与03相同
String PATH = "/Users/**/Documents/IdeaProjects/guo_poi/src/main/resources/data/" ;

    @Test  //07大数据读取 耗时短
    public void testWrite07BigDataS() throws IOException{
        //时间差 begin 和 end 的差值
        long begin = System.currentTimeMillis();

        // 创建工作簿
        Workbook workbook = new SXSSFWorkbook(); //07版本对象 XSSF
        // 创建工作表
        Sheet sheet = workbook.createSheet();
        // 写入数据
        for( int rowNum = 0; rowNum < 100000; rowNum++){
            Row row = sheet.createRow(rowNum);    // 行
            for (int cellNum = 0; cellNum <10; cellNum++){  //这里设置了10列
                Cell cell = row.createCell(cellNum);   // 列
                cell.setCellValue(cellNum);  //循环65536行,每一行都是10列的数据
            }
        }
        //运行完毕,输出文件流
        System.out.println("over");
        FileOutputStream outputStream = new FileOutputStream(PATH + "testWrite07BigDATAS.xlsx" );
        workbook.write(outputStream);  //写入
        // 关闭流
        outputStream.close();
        
        //清除临时文件!
        ((SXSSFWorkbook)workbook).dispose();
        
        //时间差
        long end = System.currentTimeMillis();
        //计算时间差,强制转换double,按秒来
        System.out.println((double)(end - begin)/1000);
    }
POI-Excel 读
// workbook 代表工作簿,使用excel能 *** 作的功能,workbook都可以 *** 作
workbook.createCellStyle();//创建样式
workbook.createName();//创建名称
workbook.findFont();//找内容
workbook.getAllPictures();//得到一些图片
workbook.isHidden();//隐藏
workbook.removeName();//移除名字


//sheet 代表表中的设置 使用excel表能 *** 作的功能,sheet都可以 *** 作
sheet.createRow();//创建行
sheet.autoSizeColumn();//列的大小
sheet.getColumnWidth();//列的宽度
sheet.getDefaultRowHeightInPoints();//行间距
03版本,读数据:
 @Test//03版本读数据
    public void testRead03() throws Exception{

        //获取文件流
        FileInputStream inputStream =  new FileInputStream(PATH +"guo_poi03版本统计表.xls");

        // 1.创建一个工作簿 使用excel能 *** 作的功能,workbook都可以 *** 作
        Workbook workbook = new HSSFWorkbook(inputStream); // 流获取到工作簿
        // 2.得到表  表中的设置  
        Sheet sheet = workbook.getSheetAt(0); //拿去 get方法
        // 3.得到行
        Row row = sheet.getRow(0);   //得到第一行
        // 4.得到列
        Cell cell  = row.getCell(0); //得到第一列
        
        //读取值时需要类型判断!
        // 获得字符串
        System.out.println(cell.getStringCellValue());  //字符串类型 
                                                        //cell.getNumericCellValue()获取数字
        // 关闭流
        inputStream.close();
    }
07版本,读数据:
@Test  //07版本读数据
    public void testRead07() throws Exception{
        //获取文件流
        FileInputStream inputStream =  new FileInputStream(PATH +"guo_poi07版本统计表.xlsx");

        // 1.创建一个工作簿 使用excel能 *** 作的功能,workbook都可以 *** 作
        Workbook workbook = new XSSFWorkbook(inputStream); // 流获取到工作簿
        // 2.得到表  表中的设置
        Sheet sheet = workbook.getSheetAt(0); //拿去 get方法
        // 3.得到行
        Row row = sheet.getRow(0);   //得到第一行
        // 4.得到列
        Cell cell  = row.getCell(0); //得到第一列
        //读取值时需要类型判断!
        // 获得字符串
        System.out.println(cell.getNumericCellValue()); //获取数字
        // 关闭流
        inputStream.close();
    }

注意获取值的类型;

读取不同数据类型:(最麻烦)注意类型转换
 @Test //测试类型
    public void testCellType() throws Exception{
        //获取文件流
        FileInputStream inputStream =  new FileInputStream(PATH +"会员消费商品明细表.xls");

        // 创建一个工作簿 使用excel能 *** 作的这边都可以 *** 作
        Workbook workbook = new HSSFWorkbook(inputStream);  //03版本
        //获取表
        Sheet sheet = workbook.getSheetAt(0);
        //获取标题内容
        Row rowTitle = sheet.getRow(0); //最上面一行
        if(rowTitle != null){
            //一定要掌握  拿到所有的列
            int cellCount = rowTitle.getPhysicalNumberOfCells(); // 获取所有列数据,总数 比如:13个列
            for (int cellNum = 0; cellNum < cellCount;cellNum++){
                Cell cell = rowTitle.getCell(cellNum);
                if (cell != null){
                    CellType cellType = cell.getCellType(); //取出数据的类型
                    String cellValue = cell.getStringCellValue(); //获取数据 该数据是string 字符串类型
                    System.out.print(cellValue + "|"); //把数据打印出来,输出时不换行
                }
            }
            System.out.println();
        }

        //获取表中的内容
         int rowCount = sheet.getPhysicalNumberOfRows(); //获取所有的行 getPhysicalNumberOfCells()获取所有列
        for( int rowNum = 1;rowNum< rowCount;rowNum++){
            Row rowData = sheet.getRow(rowNum);
            if(rowData != null){  //判空
                //读取列
                int cellCoumt = rowTitle.getPhysicalNumberOfCells();
                for (int cellNum = 0;cellNum < cellCoumt ; cellNum++){
                    System.out.print("["+(rowNum+1)+"-"+(rowNum+1)+"]");
                    Cell cell = rowData.getCell(cellNum);
                    // 匹配列的数据类型
                    if(cell != null){
                        CellType cellType = cell.getCellType();
                        String cellValue = "";

                        switch(cellType){
                            case STRING: //字符串类型
                                System.out.print("[String]");
                                cellValue = cell.getStringCellValue();
                                break;
                            case BOOLEAN: //布尔类型
                                System.out.print("[BOOLEAN]");
                                cellValue = String.valueOf(cell.getBooleanCellValue());
                                break;
                            case BLANK: //空
                                System.out.print("[BLANK]");
                                break;
                            case _NONE:
                                System.out.print("[NONE]");
                                break;
                            case NUMERIC: //数字(日期、普通数字)
                                System.out.print("[NUMERIC]");
                                if(DateUtil.isCellInternalDateFormatted(cell)) { //日期
                                    System.out.print("[日期]");
                                    Date date = cell.getDateCellValue();
                                    cellValue = new DateTime(date).toString("yyyy-MM-dd");
                                }else{
                                    //不是日期格式,防止数字过长!
                                    System.out.print("[转换为字符串输出]");
                                    cellValue = cell.toString();
                                }
                                break;
                            case ERROR: //错误
                                System.out.print("【数据类型错误】");
                                break;
                        }
                        System.out.println(cellValue);
                    }

                }
            }
        }
        inputStream.close(); //关闭流
      }
 @Test //测试类型 提取成工具类
    public void testCellType(FileInputStream inputStream) throws Exception{

        // 创建一个工作簿 使用excel能 *** 作的这边都可以 *** 作
        Workbook workbook = new HSSFWorkbook(inputStream);  //03版本
        //获取表
        Sheet sheet = workbook.getSheetAt(0);
        //获取标题内容
        Row rowTitle = sheet.getRow(0); //最上面一行
        if(rowTitle != null){
            //一定要掌握  拿到所有的列
            int cellCount = rowTitle.getPhysicalNumberOfCells(); // 获取所有列数据,总数 比如:13个列
            for (int cellNum = 0; cellNum < cellCount;cellNum++){
                Cell cell = rowTitle.getCell(cellNum);
                if (cell != null){
                    CellType cellType = cell.getCellType(); //取出数据的类型
                    String cellValue = cell.getStringCellValue(); //获取数据 该数据是string 字符串类型
                    System.out.print(cellValue + "|"); //把数据打印出来,输出时不换行
                }
            }
            System.out.println();
        }

        //获取表中的内容
        int rowCount = sheet.getPhysicalNumberOfRows(); //获取所有的行 getPhysicalNumberOfCells()获取所有列
        for( int rowNum = 1;rowNum< rowCount;rowNum++){
            Row rowData = sheet.getRow(rowNum);
            if(rowData != null){  //判空
                //读取列
                int cellCoumt = rowTitle.getPhysicalNumberOfCells();
                for (int cellNum = 0;cellNum < cellCoumt ; cellNum++){
                    System.out.print("["+(rowNum+1)+"-"+(rowNum+1)+"]");
                    Cell cell = rowData.getCell(cellNum);
                    // 匹配列的数据类型
                    if(cell != null){
                        CellType cellType = cell.getCellType();
                        String cellValue = "";

                        switch(cellType){
                            case STRING: //字符串类型
                                System.out.print("[String]");
                                cellValue = cell.getStringCellValue();
                                break;
                            case BOOLEAN: //布尔类型
                                System.out.print("[BOOLEAN]");
                                cellValue = String.valueOf(cell.getBooleanCellValue());
                                break;
                            case BLANK: //空
                                System.out.print("[BLANK]");
                                break;
                            case _NONE:
                                System.out.print("[NONE]");
                                break;
                            case NUMERIC: //数字(日期、普通数字)
                                System.out.print("[NUMERIC]");
                                if(DateUtil.isCellInternalDateFormatted(cell)) { //日期
                                    System.out.print("[日期]");
                                    Date date = cell.getDateCellValue();
                                    cellValue = new DateTime(date).toString("yyyy-MM-dd");
                                }else{
                                    //不是日期格式,防止数字过长!
                                    System.out.print("[转换为字符串输出]");
                                    cellValue = cell.toString();
                                }
                                break;
                            case ERROR: //错误
                                System.out.print("【数据类型错误】");
                                break;
                        }
                        System.out.println(cellValue);
                    }

                }
            }
        }
        inputStream.close(); //关闭流
    }
读取并计算公式:

表格样式:

 @Test //公式
    public void testFormula() throws Exception{
        //获取文件流
        FileInputStream inputStream =  new FileInputStream(PATH +"公式.xls");
        // 创建一个工作簿 使用excel能 *** 作的这边都可以 *** 作
        Workbook workbook = new HSSFWorkbook(inputStream);  //03版本
        //获取表
        Sheet sheet = workbook.getSheetAt(0);
        //获取第五行
        Row row = sheet.getRow(4);
        //获取第一列
        Cell cell = row.getCell(0);
        
        //拿到计算公式 eval函数 通过它来计算
        FormulaEvaluator formulaEvaluator =  new HSSFFormulaEvaluator((HSSFWorkbook)workbook);
        
        //输出单元格内容
        CellType cellType = cell.getCellType();
        switch (cellType){
            case FORMULA: //公式类型   需要和公式类型匹配,如果不匹配不会进入方法的
                String formula = cell.getCellFormula(); 
                System.out.println(formula);
                
                //计算
                CellValue evaluate = formulaEvaluator.evaluate(cell); // 用evaluate(cell)方法计算
                String cellValue = evaluate.formatAsString();//计算结果,列的值,格式化成字符串来输出
                System.out.println(cellValue); //输出结果
                break;
        }
    }
EasyExcel EasyExcel:快速、简洁、解决大文件内存溢出的java处理Excel工具 POI一般底层仍会使用,弊端就是当数据量大的时候会报OOM异常,相比较POI相对简单一些!

EasyExcel :
EasyExcel官方地址:https://github.com/alibaba/easyexcel

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

GitHub EasyExcel :

官方文档:

官方文档:https://www.yuque.com/easyexcel/doc/easyexcel

EasyExcel是阿里巴巴开源的一个excel处理框架,以使用简单、节省内存存储著称。
EasyExcel 能大大减少占用内存的主要原因是在解析Excel是没有将数据一次性全部加载到内存中,而且是从磁盘上一行行读取数据,逐个解析。
内存问题:POI = 100w先加载到内存中(十分耗内存)再一次性写入文件中,当内存不够时可能会报OOM。而EasyExcel在写入文件中是通过磁盘一行一行读取的。
下图是EasyExcel和POI在解析Excel时的对比图。

pom.xml 依赖
  
        <dependency>
            <groupId>com.alibabagroupId>
            <artifactId>easyexcelartifactId>
            <version>3.0.5version>
        dependency>
       

        
        <dependency>
            <groupId>org.projectlombokgroupId>
            <artifactId>lombokartifactId>
            <version>1.18.22version>
        dependency>

        <dependency>
            <groupId>com.alibabagroupId>
            <artifactId>fastjsonartifactId>
            <version>1.2.62version>
        dependency>
实体类:EasyExcel会根据实体类自动生成表

package easy;

import com.alibaba.excel.annotation.ExcelIgnore;
import com.alibaba.excel.annotation.ExcelProperty;
import lombok.Data;

import java.util.Date;

@Data
public class DemoData {
    @ExcelProperty("字符串标题")
    private String string;
    @ExcelProperty("日期标题")
    private Date date;
    @ExcelProperty("数字标题")
    private Double doubleData;
    /**
     * 忽略这个字段
     */
    @ExcelIgnore
    private String ignore;
}
实现类:写数据
package easy;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.util.ListUtils;
import org.junit.Test;

import java.util.Date;
import java.util.List;

public class TestExcel {

    String PATH = "/Users/**/Documents/IdeaProjects/guo_poi/src/main/resources/" ;//设置固定文件路径

    //自动生成10个随机数,再把随机数放入list
    private List<DemoData> data() {   //实体类加载过来
        List<DemoData> list = ListUtils.newArrayList();
        for (int i = 0; i < 10; i++) {
            DemoData data = new DemoData();
            //设初值,返回到list中
            data.setString("字符串" + i);  
            data.setDate(new Date());
            data.setDoubleData(0.56);
            list.add(data);
        }
        return list;
    }

    //根据list 写入excel
    /**
     * 最简单的写
     * 

* 1. 创建excel对应的实体对象 参照{@link DemoData} *

* 2. 直接写即可 */ @Test public void simpleWrite() { // 注意 simpleWrite在数据量不大的情况下可以使用(5000以内,具体也要看实际情况),数据量大参照 重复多次写入 // 最简单写法 JDK8+ 其余写法官方文档上都有 String fileName = PATH + "dataEasyTest.xlsx"; // 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭 // write.(fileName,格式类) 格式类会根据注释自动生成表的属性 // sheet("表名") 生成表的方法 //dowrite.(数据) 写入数据 从前端或者数据库读取的数据 EasyExcel.write(fileName, DemoData.class) //filename 数据流,DemoData.class 对应实体 标题生成 .sheet("模板") //生成一个sheet为模版的表 .doWrite(data());//dowrite(数据) //data() 这里就是写了一个简单的生成随机数,真实情况是从前端返过来或数据库中读取出来的一个list,绑定mapper就行 } }

成功运行:可以看到生成了dataEasyExcel.xlsx

生成的数据

实现类:读数据
 //只写了方法,该方法也放在TestExcel中
    /**
     * 最简单的读
     * 

* 1. 创建excel对应的实体对象 参照{@link DemoData} *

* 2. 由于默认一行行的读取excel,所以需要创建excel一行一行的回调监听器,参照{@link DemoDataListener} *

* 3. 直接读即可 */ @Test public void simpleRead() { // 有个很重要的点 DemoDataListener 不能被spring管理,要每次读取excel都要new,然后里面用到spring可以构造方法传进去 // 最简单写法 JDK8+ 其余写法官方文档上都有 String fileName = PATH + "dataEasyExcel.xlsx"; // 这里 需要指定读哪个文件,用哪个class按格式输出,通过DemoDataListener监视器去判断逻辑然后读取第一个sheet 文件流会自动关闭 EasyExcel.read(fileName, DemoData.class, new DemoDataListener()).sheet().doRead(); }

持久层:DAO
package easy;

import java.util.List;

/**
 * 假设这个是你的DAO存储。当然还要这个类让spring管理,当然你不用需要存储,也不需要这个类。
 **/
public class DemoDAO {
    public void save(List<DemoData> list) {
        //持久化 *** 作,保存数据库!
        // 如果是mybatis,尽量别直接调用多次insert,自己写一个mapper里面新增一个方法batchInsert,所有数据一次性插入
    }
}
DemoDataListener:
package easy;

import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.List;

// 有个很重要的点 DemoDataListener 不能被spring管理,要每次读取excel都要new,然后里面用到spring可以构造方法传进去
@Slf4j
public class DemoDataListener extends AnalysisEventListener<DemoData> {

    private static final Logger LOGGER = LoggerFactory.getLogger(DemoDataListener.class);

    /**
     * 每隔5条存储数据库,实际使用中可以100条,然后清理list ,方便内存回收
     */
    private static final int BATCH_COUNT = 5;
    private List<DemoData> list = new ArrayList<DemoData>();

    //sprin 需要注入时使用
    /**
     * 假设这个是一个DAO,当然有业务逻辑这个也可以是一个service。当然如果不用存储这个对象没用。
     */
    private DemoDAO demoDAO;

    public DemoDataListener() {
        // 这里是demo,所以随便new一个。实际使用如果到了spring,请使用下面的有参构造函数
        demoDAO = new DemoDAO();
    }

    /**
     * 如果使用了spring,请使用这个构造方法。每次创建Listener的时候需要把spring管理的类传进来
     *
     * @param demoDAO
     */
    public DemoDataListener(DemoDAO demoDAO) {
        this.demoDAO = demoDAO;
    }


    //读取数据时会执行 invoke 方法
    // 读入
    // DemoData 类型
    // AnalysisContext 分析器 用来分析上下文
    /**
     * 这个每一条数据解析都会来调用 invoke 方法
     *
     * @param data    one row value. Is is same as {@link AnalysisContext#readRowHolder()}
     * @param context
     */
    @Override
    public void invoke(DemoData data, AnalysisContext context) {
        //fastjson 导入Maven依赖
        System.out.println(JSON.toJSONString(data));  //读取到的每一条数据都会变成 JSON字符串
        list.add(data);
        // 达到BATCH_COUNT了,需要去存储一次数据库,防止数据几万条数据在内存,容易OOM
        if (list.size() >= BATCH_COUNT) {
            saveData(); //保存数据库  这里是空的
            // 存储完成清理 list
            list.clear();
        }
    }

    /**
     * 所有数据解析完成了 都会来调用
     *
     * @param context
     */
    @Override
    public void doAfterAllAnalysed(AnalysisContext context) {
        // 这里也要保存数据,确保最后遗留的数据也存储到数据库
        saveData();
        log.info("所有数据解析完成!");
    }

    /**
     * 加上存储数据库
     */
    private void saveData() {
        log.info("{}条数据,开始存储数据库!", list.size());
        demoDAO.save(list);
        log.info("存储数据库成功!");
    }
}


固定套路:

1. 写入,固定类格式的进行写入!
2. 读取,根据监听器设置的规则进行读取!
一切代码都在官方文档

官方文档:
官方文档:https://www.yuque.com/easyexcel/doc/easyexcel

欢迎分享,转载请注明来源:内存溢出

原文地址: http://www.outofmemory.cn/langs/730996.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-04-27
下一篇 2022-04-27

发表评论

登录后才能评论

评论列表(0条)

保存