POI与EasyExcel的简单使用

POI的使用

1.创建Maven项目,导入依赖

    <dependencies>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>easyexcel</artifactId>
            <version>2.2.7</version>
        </dependency>

        <!--日期格式化工具-->
        <dependency>
            <groupId>joda-time</groupId>
            <artifactId>joda-time</artifactId>
            <version>2.10.1</version>
        </dependency>

        <!--        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>

        <!--test-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
    </dependencies>

xls03与xlsx07的区别:写入的数据量大小,03版最多只能写65536条数据

2.创建测试类

Workbook接口共用三个实现类
在这里插入图片描述
HSSFWorkbook:用于处理03xls的Excel数据
XSSFWorkbook:用于处理07xlsx的Excel数据
SXSSFWorkbook:升级版的XSSFWorkbook,处理速度快

3.基本的写入操作:

因为07版于03版之间的使用区别就是WorkBook的实现类不同,以及创建IO流时文件的后缀名不同,这里就只列出03版的使用案例

    @Test
    public void test03() throws Exception {
//        1、创建工作薄
        Workbook workbook = new HSSFWorkbook();
//        2、创建工作表
        Sheet sheet = workbook.createSheet("测试1");
//        3、创建一个行 (1,1)
        Row row = sheet.createRow(0);
//        4、创建单元格
        Cell cell11 = row.createCell(0);
        cell11.setCellValue("统计人数");
//        (1,2)
        Cell cell12 = row.createCell(1);
        cell12.setCellValue(666);
//        第二行 (2,1)
        Cell cell21 = row.createCell(1);
        cell21.setCellValue("统计时间");
//        (2,2)
        Cell cell22 = row.createCell(1);
        String time = new DateTime().toString("yyyy-MM-dd HH:mm:ss");
        cell22.setCellValue(time);
//		创建输出流
        FileOutputStream os = new FileOutputStream("D:/03.xls");
        
        workbook.write(os);
        os.close();
        System.out.println("导出完成");
    }

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

在这里插入图片描述
4.数据批量导入

大文件写入HSSF

缺点:最多只能处理65536行否则会抛出异常
优点:过程中写入缓存,不操作磁盘,最后一次性写入磁盘,速度快

大文件写入XSSF

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

大文件写SXSSF

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

下面我将测试03版于07在操作大数据时所耗的时间

03版

    @Test
    public void test03BigData() throws Exception {
        long start = System.currentTimeMillis();
//         1、创建工作薄
        Workbook workbook = new HSSFWorkbook();
//        2、创建工作表
        Sheet sheet = workbook.createSheet("测试1");
        for (int rowNum = 0; rowNum < 65537; rowNum++) {
            Row row = sheet.createRow(rowNum);
            for (int i = 0; i < 10; i++) {
                Cell cell = row.createCell(i);
                cell.setCellValue(i);
            }
        }
        FileOutputStream os = new FileOutputStream("D:\\JavaLearn\\javaSE\\ExcelTest\\xiao-poi/03big.xls");
        workbook.write(os);
        os.close();
        long end=System.currentTimeMillis();
        System.out.println("耗时:"+(double)(end-start)/1000);
    }

耗时:
在这里插入图片描述
07版

@Test
    public void test07BigData() throws Exception {
        long start = System.currentTimeMillis();
//         1、创建工作薄
        Workbook workbook = new XSSFWorkbook();
//        2、创建工作表
        Sheet sheet = workbook.createSheet("测试1");
        for (int rowNum = 0; rowNum < 65536; rowNum++) {
            Row row = sheet.createRow(rowNum);
            for (int i = 0; i < 10; i++) {
                Cell cell = row.createCell(i);
                cell.setCellValue(i);
            }
        }
        FileOutputStream os = new FileOutputStream("D:\\JavaLearn\\javaSE\\ExcelTest\\xiao-poi/07big.xlsx");
        workbook.write(os);
        os.close();
        long end=System.currentTimeMillis();
        System.out.println("耗时:"+(double)(end-start)/1000);
    }

耗时:
在这里插入图片描述
那么当数据量大于65536时两种方法将会怎样。
经过我的测试以后发现
03版将会发生OOM异常,提示内存溢出
在这里插入图片描述
07版所操作数据的时间将大大提升。
在这里插入图片描述
那么我们再使用SXSSFWorkbook看看会怎样。

    @Test
    public void test07BigDatas() throws Exception {
        long start = System.currentTimeMillis();
//         1、创建工作薄
        SXSSFWorkbook workbook = new SXSSFWorkbook();
//        2、创建工作表
        Sheet sheet = workbook.createSheet("测试1");
        for (int rowNum = 0; rowNum < 100000; rowNum++) {
            Row row = sheet.createRow(rowNum);
            for (int i = 0; i < 10; i++) {
                Cell cell = row.createCell(i);
                cell.setCellValue(i);
            }
        }
        FileOutputStream os = new FileOutputStream("D:\\JavaLearn\\javaSE\\ExcelTest\\xiao-poi/07bigs.xlsx");
        workbook.write(os);
        os.close();
		//清除临时文件
        workbook.dispose();
        long end=System.currentTimeMillis();
        System.out.println("耗时:"+(double)(end-start)/1000);
    }

在这里插入图片描述
注意:过程中会产生临时文件,需要清理临时文件,默认有100条记录被保存到内存中,如果超过这数量,则最前面的数据会被写入临时文件。
如果像自定义内存中数据的数量,可以使用new SXSSFWorkbook(数量)

5.Excel基本读取

    @Test
    public void testRead03() throws Exception {
        //获取文件流
        FileInputStream is = new FileInputStream("D:/03.xls");
        //1.创建一个工作薄。使用Excel能操作的这边也都能操作
        Workbook workbook = new HSSFWorkbook(is);
        //2.得到表
        Sheet sheet = workbook.getSheetAt(0);//也可以通过表名去获取
        //3.得到行
        Row row = sheet.getRow(0);
        //4.得到列
        Cell cell = row.getCell(0);
        System.out.println(cell.getStringCellValue());
    }

测试结果:
在这里插入图片描述
6.读取不同的数据类型

首先准备一个Excel的文件
在这里插入图片描述
测试案例

 @Test
    public void testCellType() throws Exception {
        //获取文件流
        FileInputStream is = new FileInputStream("D:/测试表.xlsx");
        //1.创建一个工作薄。使用Excel能操作的这边也都能操作
        Workbook workbook = new XSSFWorkbook(is);
        Sheet sheet = workbook.getSheetAt(0);
        //2.获取标题内容
        Row rowTitle = sheet.getRow(0);
        if (rowTitle != null) {
            int titleCount = rowTitle.getPhysicalNumberOfCells();
            for (int i = 0; i < titleCount; i++) {
                Cell cell = rowTitle.getCell(i);
                if (cell != null) {
                    System.out.println(cell.getStringCellValue() + "|");
                }
            }
        }
        //3.获取表中内容
        //统计一个表中有多少行
        int rowCount = sheet.getPhysicalNumberOfRows();
        for (int i = 1; i < rowCount; i++) {
            Row row = sheet.getRow(i);
            //统计一行中有多少列
            int cellCount = row.getPhysicalNumberOfCells();
            for (int j = 0; j < cellCount; j++) {
                Cell cell = row.getCell(j);
                //匹配列的数据类型
                if (cell != null) {
                    CellType cellType = cell.getCellTypeEnum();
                    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 NUMERIC:   //数字 分为:日期、普通数字
                            System.out.print("【Numeric】");
                            if (HSSFDateUtil.isCellDateFormatted(cell)) {
                                System.out.print("【日期】");
                                cellValue = new DateTime(cell.getDateCellValue()).toString("yyyy-MM-dd HH:mm:ss");
                            } else {
                                //不是日期格式,为了防止数字过长,转换为字符串输出
                                System.out.print("【转换为字符串输出】");
                                cell.setCellType(STRING);
                                cellValue = cell.toString();
                            }
                            break;
                        case ERROR:
                        default:
                            break;
                    }
                    System.out.println(cellValue);
                }
            }
        }
        is.close();
    }

结果

姓名|年龄|性别|手机号|消费日期|商品编号|商品名称|商品单位|原价|销售价|销售数量|销售金额|是否上架|
[2-1]【String】张三
[2-2]【Numeric】【转换为字符串输出】19
[2-3]【String】男
[2-4]【Numeric】【转换为字符串输出】12414332
[2-5]【Numeric】【日期】2021-03-23 00:00:00
[2-6]【String】PV700012
[2-7]【String】营养快线
[2-8]【String】瓶
[2-9]【Numeric】【转换为字符串输出】2
[2-10]【Numeric】【转换为字符串输出】4
[2-11]【Numeric】【转换为字符串输出】10
[2-12]【Numeric】【转换为字符串输出】1000
[2-13]【Boolean】true

easyExcel使用

1、导入依赖

    <dependencies>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>easyexcel</artifactId>
            <version>2.2.7</version>
        </dependency>

        <!--日期格式化工具-->
        <dependency>
            <groupId>joda-time</groupId>
            <artifactId>joda-time</artifactId>
            <version>2.10.1</version>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.12</version>
            <scope>provided</scope>
        </dependency>

        <!--test-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
    </dependencies>

2、编写实体类

@Data注解需要导入Lombok依赖

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

简单的写Excel

//    生成通用数据
    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;
    }

    /**
     * 根据list 写入excel
     */
    @Test
    public void simpleWrite() {
        String fileName = "D:/JavaLearn/javaSE/ExcelTest/xiao-poi/easyTest.xlsx";
        // 这里 需要指定写用哪个class去读,然后写到第一个sheet,名字为模板 然后文件流会自动关闭
        //write(fileName,格式类)
        //sheet(表名)
        //doWrite(数据)
        EasyExcel.write(fileName, DemoData.class).sheet("模板").doWrite(data());
    }

效果
在这里插入图片描述

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

简单的读Excel

1、编写监听器

// 有个很重要的点 DemoDataListener 不能被spring管理,要每次读取excel都要new,然后里面用到spring可以构造方法传进去
public class DemoDataListener extends AnalysisEventListener<DemoData> {
    private static final Logger LOGGER = LoggerFactory.getLogger(DemoDataListener.class);
    /**
     * 每隔5条存储数据库,实际使用中可以3000条,然后清理list ,方便内存回收
     */
    private static final int BATCH_COUNT = 5;
    List<DemoData> list = new ArrayList<DemoData>();
    /**
     * 假设这个是一个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 分析上下文
     */

    @Override
    public void invoke(DemoData data, AnalysisContext context) {
        System.out.println(JSON.toJSONString(data));
        list.add(data);
        // 达到BATCH_COUNT了,需要去存储一次数据库,防止数据几万条数据在内存,容易OOM
        if (list.size() >= BATCH_COUNT) {
            saveData(); //持久化逻辑
            // 存储完成清理 list
            list.clear();
        }
    }

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

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

编写Dao层,如果不需要存储到数据库中,也可以不写

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

测试读取

    @Test
    public void simpleRead() {
        String fileName = "D:/JavaLearn/javaSE/ExcelTest/xiao-poi/easyTest.xlsx";
        // 有个很重要的点 DemoDataListener 不能被spring管理,要每次读取excel都要new,然后里面用到spring可以构造方法传进去
        // 这里 需要指定读用哪个class去读,然后读取第一个sheet 文件流会自动关闭
        EasyExcel.read(fileName, DemoData.class, new DemoDataListener()).sheet().doRead();
    }

测试结果
在这里插入图片描述
官方文档链接:https://www.yuque.com/easyexcel/doc/read

固定套路:
1.写入:固定类格式进行写入
2.读取:根据监听器设置的规则进行读取

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值