POI和EasyExcel读写Excel 操作

1.POI读写Excel 数据

1.1 导入依赖

03和07版本的写,就是对象不同,方法一样

最大行列得数量不同:

xls最大只有65536行、256

xlsx可以有1048576行、16384

        <!--xls(03)-->
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi</artifactId>
            <version>3.9</version>
        </dependency>
        
        <!--xlsx(07)-->
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi-ooxml</artifactId>
            <version>3.9</version>
        </dependency>

poi 操作xls
poi-ooxml 操作xlsx

操作的版本不同,使用的工具类也不同
在这里插入图片描述

1.2 POI Excel 写 03和07版本方式(小数据量)

package cn.bloghut;

import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.joda.time.DateTime;
import org.junit.jupiter.api.Test;

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.util.Date;


public class ExcelWrite {
    
    

    String PATH = "G:\\狂\\POIStudy\\xy-poi";

    /**
     * 写工作簿 03版本
     */
    @Test
    public void Write03() throws Exception {
    
    
        //1.创建一个工作簿
        Workbook workbook = new HSSFWorkbook();
        //2.创建 一个工作表
        Sheet sheet = workbook.createSheet("闲言粉丝统计表");
        //3.创建一行
        Row row1 = sheet.createRow(0);
        //4.创建一个单元格
        //(1,1)
        Cell cell1 = row1.createCell(0);
        cell1.setCellValue("今日新增观众");
        //(1,2)
        Cell cell2 = row1.createCell(1);
        cell2.setCellValue(666);

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

        //生成一张表(IO流),03版本就是使用xls结尾
        FileOutputStream fos = new FileOutputStream(PATH + "闲言观众统计表03.xls");
        //输出
        workbook.write(fos);
        //关闭流
        fos.close();
        System.out.println("文件生成完毕");
    }

    /**
     * 写工作簿 07版本
     */
    @Test
    public void Write07() throws Exception {
    
    
        //1.创建一个工作簿
        Workbook workbook = new XSSFWorkbook();
        //2.创建 一个工作表
        Sheet sheet = workbook.createSheet("闲言粉丝统计表");
        //3.创建一行
        Row row1 = sheet.createRow(0);
        //4.创建一个单元格
        //(1,1)
        Cell cell1 = row1.createCell(0);
        cell1.setCellValue("今日新增观众");
        //(1,2)
        Cell cell2 = row1.createCell(1);
        cell2.setCellValue(666);

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

        //生成一张表(IO流),03版本就是使用xlsx结尾
        FileOutputStream fos = new FileOutputStream(PATH + "闲言观众统计表07.xlsx");
        //输出
        workbook.write(fos);
        //关闭流
        fos.close();
        System.out.println("文件生成完毕");
    }
}

1.3 大文件写HSSF(03版)

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

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

在这里插入图片描述
优点:过程中写入缓存,不操作磁盘,最后一次性吸入磁盘,速度快

@Test
    public void Write03BigData() throws Exception{
    
    

        //时间
        long begin = System.currentTimeMillis();
        //1.创建一个工作簿
       Workbook workbook = new HSSFWorkbook();
        //2.创建一个表
        Sheet sheet = workbook.createSheet("第一页");
        //写入数据
        for (int rowNum = 0;rowNum<65536;rowNum++){
    
    
            //3.创建行
            Row row = sheet.createRow(rowNum);
           for (int CellNum = 0;CellNum<10;CellNum++){
    
    
               Cell cell = row.createCell(CellNum);
               cell.setCellValue(CellNum);
           }
        }
        System.out.println("over");
        //获取io流
        FileOutputStream fos = new FileOutputStream(PATH+"Write03BigData.xls");
        //生成一张表
        workbook.write(fos);
        fos.close();
        long end = System.currentTimeMillis();
        System.out.println("耗时:"+(end-begin));
    }

结果:
在这里插入图片描述

1.4 大文件写XSSF(07版)

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

    @Test
    public void Write07BigData() throws Exception{
    
    

        //时间
        long begin = System.currentTimeMillis();
        //1.创建一个工作簿
        Workbook workbook = new XSSFWorkbook();
        //2.创建一个表
        Sheet sheet = workbook.createSheet("第一页");
        //写入数据
        for (int rowNum = 0;rowNum<65537;rowNum++){
    
    
            //3.创建行
            Row row = sheet.createRow(rowNum);
            for (int CellNum = 0;CellNum<10;CellNum++){
    
    
                Cell cell = row.createCell(CellNum);
                cell.setCellValue(CellNum);
            }
        }
        System.out.println("over");
        //获取io流
        FileOutputStream fos = new FileOutputStream(PATH+"Write03BigData.xlsx");
        //生成一张表
        workbook.write(fos);
        fos.close();
        long end = System.currentTimeMillis();
        System.out.println("耗时:"+(end-begin));
    }

结果:
在这里插入图片描述

1.5 大文件写SXSSF

优点:可以写非常大的数据量,如100万条甚至更多条写数据速度快,占用更少的内存

注意:

过程中会产生临时文件,需要清理临时文件
默认由100条记录被保存在内存中,如果超过这数量,则最前面的数据被写入临时文件
如果想自定义内存中数据的数量,可以使用new SXSSFWorkbook(数量)

@Test
    public void Write07BigDataS() throws Exception{
    
    

        //时间
        long begin = System.currentTimeMillis();
        //1.创建一个工作簿
        Workbook workbook = new SXSSFWorkbook();
        //2.创建一个表
        Sheet sheet = workbook.createSheet("第一页");
        //写入数据
        for (int rowNum = 0;rowNum<100000;rowNum++){
    
    
            //3.创建行
            Row row = sheet.createRow(rowNum);
            for (int CellNum = 0;CellNum<10;CellNum++){
    
    
                Cell cell = row.createCell(CellNum);
                cell.setCellValue(CellNum);
            }
        }
        System.out.println("over");
        //获取io流
        FileOutputStream fos = new FileOutputStream(PATH+"Write03BigDataS.xlsx");
        //生成一张表
        workbook.write(fos);
        fos.close();
        //清除临时文件
        ((SXSSFWorkbook) workbook).dispose();
        long end = System.currentTimeMillis();
        System.out.println("耗时:"+(end-begin));
    }

SXSSFWorkbook-来至官方的解释︰实现"BigGridDemo"策略的流式XSSFWorkbook版本。这允许写入非常大的文件而不会耗尽内存,因为任何时候只有可配置的行部分被保存在内存中。

请注意,仍然可能会消耗大量内存,这些内存基于您正在使用的功能,例如合并区域,注.…….当然只存储在内存中,因此如果广泛使用,可能需要大量内存。
再使用POI的时候!内存问题Jprofile !

2.POI Excel读 03和07版本方式

2.1 读取数据(03版本)

 @Test
    public  void Read03() throws Exception{
    
    
        //1.获取文件流
        FileInputStream fis = new FileInputStream(PATH+"xy-poi闲言观众统计表03.xls");
        //2.创建一个工作簿。使用excel能操作的这边都可以操作!
        Workbook workbook = new HSSFWorkbook(fis);
        //3.获取表
        Sheet sheet = workbook.getSheetAt(0);
        //4.获取第一行
        Row row1 = sheet.getRow(0);
        //5.获取第一列
        Cell cell1 = row1.getCell(0);
        //6.获取第一行第一列的值
        String stringCellValue = cell1.getStringCellValue();
        //获取第二列
        Cell cell2 = row1.getCell(1);
        //获取第一行第二列的值
        double numericCellValue = cell2.getNumericCellValue();
        System.out.println(stringCellValue+" | "+numericCellValue);

        fis.close();
    }

注意获取值的类型即可

2.2 读取数据(07版本)

@Test
    public  void Read07() throws Exception{
    
    
        //1.获取文件流
        FileInputStream fis = new FileInputStream(PATH+"xy-poi闲言观众统计表07.xlsx");
        //2.创建一个工作簿。使用excel能操作的这边都可以操作!
        Workbook workbook = new XSSFWorkbook(fis);
        //3.获取表
        Sheet sheet = workbook.getSheetAt(0);
        //4.获取第一行
        Row row1 = sheet.getRow(0);
        //5.获取第一列
        Cell cell1 = row1.getCell(0);
        //6.获取第一行第一列的值
        String stringCellValue = cell1.getStringCellValue();
        //获取第二列
        Cell cell2 = row1.getCell(1);
        //获取第一行第二列的值
        double numericCellValue = cell2.getNumericCellValue();
        System.out.println(stringCellValue+" | "+numericCellValue);
        fis.close();
    }

注意获取值的类型即可

2.3 读取不同的数据类型

    @Test
    public void CellType03() throws Exception{
    
    
        //获取文件流
        FileInputStream fis = new FileInputStream(PATH+"明显表.xls");
        //获取一个工作簿
        Workbook workbook = new HSSFWorkbook(fis);
        //获取一个工作表
        Sheet sheet = workbook.getSheetAt(0);
        //获取第一行内容
        Row row = sheet.getRow(0);
        if (row != null){
    
    
            //获取所有的列
            int Cells = row.getPhysicalNumberOfCells();
            for (int col = 0;col < Cells;col++){
    
    
                //获取当前列
                Cell cell = row.getCell(col);
                if (cell != null){
    
    
                    //获取当前行的第 col 列的值
                    String cellValue = cell.getStringCellValue();
                    System.out.print(cellValue+" | ");
                }
            }
        }
        //获取标准的内容
        //获取有多少行
        int rowCount = sheet.getPhysicalNumberOfRows();
        //从1开始,第一行是标题
        for (int rowNum = 1;rowNum < rowCount;rowNum++){
    
    
            Row rowData = sheet.getRow(rowNum);
            if (rowData != null){
    
    
                //获取当前行的列数
                int cellCount = rowData.getPhysicalNumberOfCells();
                System.out.println();
                for (int col = 0;col < cellCount;col++){
    
    
                    //获取当前列的值
                    Cell cellData = rowData.getCell(col);
                    //打印当前行当前列的值
                    System.out.print("["+(rowNum+1)+"-"+(col+1)+"]");
                    //匹配列的类型
                    if (cellData != null){
    
    
                        //获取列的类型
                        int cellType = cellData.getCellType();
                        String cellValue = "";
                        switch (cellType){
    
    
                            case  Cell.CELL_TYPE_STRING://字符串
                                System.out.print("[string]");
                                cellValue = cellData.getStringCellValue();
                                break;
                            case  Cell.CELL_TYPE_BOOLEAN://布尔
                                System.out.print("[boolean]");
                                cellValue = String.valueOf(cellData.getBooleanCellValue());
                                break;
                            case  Cell.CELL_TYPE_BLANK://System.out.print("[blank]");
                                break;
                            case  Cell.CELL_TYPE_NUMERIC://数字(日期、普通数字)
                                System.out.print("[numeric]");
                                if (HSSFDateUtil.isCellDateFormatted(cellData)){
    
    
                                    //如果是日期
                                    System.out.print("[日期] ");
                                    Date date = cellData.getDateCellValue();
                                    cellValue = new  DateTime(date).toString("yyyy-MM-dd HH:mm:ss");
                                }else {
    
    
                                    //不是日期格式,防止数字过长
                                    System.out.print("[转换字符串输出] ");
                                    //转为字符串
                                    cellData.setCellType(HSSFCell.CELL_TYPE_STRING);
                                    cellValue = cellData.toString();

                                }
                                break;
                            case  Cell.CELL_TYPE_ERROR://错误
                                System.out.print("[error]");
                                break;
                        }
                        System.out.print("["+cellValue+"]\n");
                    }
                }
            }
        }

        System.out.println();
        System.out.println("over");
        fis.close();
    }

如果是07版本的Excel ,只需要将HSSFWorkbook类修改为XSSFWorkbook类。将xls文件修改为xlsx文件即可

测试:读取以下表格内容
在这里插入图片描述
结果:
在这里插入图片描述

3.EasyExcel操作

3.1 导入依赖

        <!--easyExcel-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>easyexcel</artifactId>
            <version>2.2.0-beta2</version>
        </dependency>

3.2 写入测试

3.2.1 格式类

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

3.2.2 写的方法

@Test
public  void simpleWrite(){
    
    
    // 写法1
    String fileName = PATH+"EasyTest.xlsx";
    //write(fileName,格式类)
    //sheet(表名)
    //doWrite(数据)
    EasyExcel.write(fileName,DemoData.class).sheet("模板").doWrite(data());

}

结果:
在这里插入图片描述
固定套路:
1、写入:固定类格式进行写入
2、读取:根据监听器设置的规则进行读取

3.3 读测试

演示读取以下excel表格数据
在这里插入图片描述

3.3.1 格式类

@Getter
@Setter
@EqualsAndHashCode
public class DemoData {
    
    
    private String string;
    private Date date;
    private Double doubleData;
}

3.3.2 监听器

package cn.bloghut.esay;

import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.fastjson.JSON;

import java.util.ArrayList;
import java.util.List;

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

    private static final int BATCH_COUNT = 100;
    private List<DemoData> cachedDataList = new ArrayList<>(BATCH_COUNT);

    private DemoDAO demoDAO;

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

    public DemoDataListener(DemoDAO demoDAO) {
    
    
        this.demoDAO = demoDAO;
    }

    /**
     * 读取数据会执行invoke 方法
     * DemoData 类型
     * AnalysisContext 分析上下文
     *
     * @param data
     * @param context
     */
    @Override
    public void invoke(DemoData data, AnalysisContext context) {
    
    
        System.out.println(JSON.toJSONString(data));
        cachedDataList.add(data);
        // 达到BATCH_COUNT了,需要去存储一次数据库,防止数据几万条数据在内存,容易OOM
        if (cachedDataList.size() >= BATCH_COUNT) {
    
    
            saveData();
            // 存储完成清理 list
            cachedDataList.clear();
        }
    }

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

    /**
     * 加上存储数据库
     */
    private void saveData() {
    
    
        System.out.println("{}条数据,开始存储数据库!"+cachedDataList.size());
        demoDAO.save(cachedDataList);
        System.out.println("存储数据库成功");
    }

}

DAO类(不操作数据库,用不到)

public class DemoDAO {
    
    
    public void save(List<DemoData> list) {
    
    
        // 如果是mybatis,尽量别直接调用多次insert,自己写一个mapper里面新增一个方法batchInsert,所有数据一次性插入
    }
}

测试

@Test
    public void simpleRead() throws Exception{
    
    
        String fileName = PATH+"EasyTest.xlsx";
        EasyExcel.read(fileName,DemoData.class,new DemoDataListener()).sheet().doRead();

    }

结果:
在这里插入图片描述
根据狂神说Java教程整理

猜你喜欢

转载自blog.csdn.net/qq_27480007/article/details/134733089