使用Apache POI对Excel实现批量读取

Apache POI

Apache POI是Apache软件基金会的开源项目,POI提供API给Java程序对Microsoft Office格式档案读和写的功能。

依赖

		<dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi</artifactId>
            <version>4.1.2</version>
        </dependency>
        <!--        xls-07-->
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi-ooxml</artifactId>
            <version>4.1.2</version>
        </dependency>
        <!--        日期格式化工具-->
        <dependency>
            <groupId>joda-time</groupId>
            <artifactId>joda-time</artifactId>
            <version>2.10.4</version>
        </dependency>

批量数据写入

  • 03版本HSSF:

    最多支持65536行数据的写入,超出时抛异常

    操作方式:先将所有数据放入缓存,最后一次性写入磁盘,速度快

  • 07+版本XSSF:

    写入数据满,获取全部行的数据,消耗大量内存,数据量庞大时有可能发生内存溢出

    可以写入较大的数据

//03写入
public void writeBatchData03() throws IOException {
        //开始时间
        long start = System.currentTimeMillis();
        //创建工作表
        Workbook workbook = new HSSFWorkbook();
        Sheet sheet = workbook.createSheet("03");
        //写入数据
        for (int rowNum = 0;rowNum<=65535;rowNum++){
            Row row = sheet.createRow(rowNum);
            for(int cellNum = 0;cellNum<20;cellNum++){
                Cell cell = row.createCell(cellNum);
                cell.setCellValue(cellNum+1);
            }
        }
        //生成表
        FileOutputStream outputStream = new FileOutputStream("./03BatchData.xls");
        workbook.write(outputStream);
        System.out.println("表格生成完成");
        //结束时间
        long end = System.currentTimeMillis();
        System.out.println("生成时间:"+(end-start)/1000+"s");//秒

    }
//07写入
public void writeBatchData07() throws IOException {
        //开始时间
        long start = System.currentTimeMillis();
        //创建工作表
        Workbook workbook = new XSSFWorkbook();
        Sheet sheet = workbook.createSheet("03");
        //写入数据
        for (int rowNum = 0;rowNum<65536;rowNum++){
            Row row = sheet.createRow(rowNum);
            for(int cellNum = 0;cellNum<20;cellNum++){
                Cell cell = row.createCell(cellNum);
                cell.setCellValue(cellNum+1);
            }
        }
        //生成表
        FileOutputStream outputStream = new FileOutputStream("./07BatchData.xlsx");
        workbook.write(outputStream);
        System.out.println("表格生成完成");
        //结束时间
        long end = System.currentTimeMillis();
        System.out.println("生成时间:"+(end-start)/1000+"s");//秒
    }

POI操作Excel读

具体步骤:

利用文件流来进行读取

1.获取工作簿

2.获取表

3.获取行

4.获取单元格

5.读取数据

public void readExcel03() throws IOException {
        //1.通过文件流读取Excel工作簿
        FileInputStream inputStream = new FileInputStream("D:\\myfiles\\办公室\\加分模板\\11月21日前加分\\软件学院2023-2024学年第1学期11月劳动学时统计表.xls");
        //2.获取工作簿
        Workbook workbook = new HSSFWorkbook(inputStream);
        //3.获取表(通过下标的方式)
        Sheet sheet = workbook.getSheetAt(0);
        //4.获取行
        Row row = sheet.getRow(0);
        Cell cell = row.getCell(0);

        //6.读取数据
        String data = cell.getStringCellValue();
        System.out.println(data);
        //7.关闭流
        inputStream.close();
    }
public void readExcel07() throws IOException {
        //1.通过文件流读取Excel工作簿
        FileInputStream inputStream = new FileInputStream("D:\\myfiles\\办公室\\加分模板\\11月21日前加分\\(D)软件学院2023年11月“校园反诈宣传活动”参与人员.xlsx");
        //2.获取工作簿
        Workbook workbook = new XSSFWorkbook(inputStream);
        //3.获取表(通过下标的方式)
        Sheet sheet = workbook.getSheetAt(0);
        //4.获取行
        Row row = sheet.getRow(0);
        Cell cell = row.getCell(0);

        //6.读取数据
        String data = cell.getStringCellValue();
        System.out.println(data);
        //7.关闭流
        inputStream.close();
    }

批量读取数据

在这里插入图片描述

首先读取标题部分,然后再来读取具体数据部分,两步分开!

public void readExcelCellType() throws IOException {
        //1.通过文件流读取Excel工作簿
        FileInputStream inputStream = new FileInputStream("D:\\myfiles\\临时文件\\用户.xlsx");
        //2.获取工作簿
        Workbook workbook = new XSSFWorkbook(inputStream);
        //3.获取表(通过下标的方式)
        Sheet sheet = workbook.getSheetAt(0);
        //获取标题内容
        //获取表中的第一行的数据
        Row title = sheet.getRow(0);
        //非空判断
        if(title!=null){
            //获取标题的单元格数量,用于遍历,获取所有单元格
           int cellNum =  title.getPhysicalNumberOfCells(); //所有单元格的数量
            System.out.println(cellNum);
            //遍历第一行所有单元格  只适用于所有单元格是同一种数据类型的时候
            for (int i = 0; i < cellNum; i++) {
                Cell cell= title.getCell(i);
                if(cell!=null){
                    //获取单元格中的数据内容
                   String value = cell.getStringCellValue();
                    System.out.println(value);
                }
            }
        }
        //获取标题以下的具体内容
        //获取一共多少行数据
        int rowNum = sheet.getPhysicalNumberOfRows();
        //读取时跳过第一行表头的数据
        for (int i = 1; i < rowNum; i++) {
            Row row = sheet.getRow(i);//获取每一行数据
            if(row!=null){
                //获取每一行里面有多少单元格
                int cellNum = row.getPhysicalNumberOfCells();
                //遍历每一行单元格里的数据
                for (int j = 0; j < cellNum; j++) {
                    Cell cell = row.getCell(j);
                    if(cell!=null){
                        //每个单元格里的数据类型不同
//                        String val = cell.getStringCellValue();//会出问题
                        //拿到数据先判断数据类型

                        CellType cellType = cell.getCellType();
                        //读取出的单元格数据最后都会存成String数据
                        String cellval="";
                        //根据不同的类型读取数据
                        switch (cellType){
                            case STRING://字符串
                               cellval = cell.getStringCellValue();
                                break;
                            case NUMERIC://数值类型
                            //判断是否为日期
                                if(DateUtil.isCellDateFormatted(cell)){
//                                    System.out.println("当前单元格是日期类型");
                                    Date date = cell.getDateCellValue();
                                    cellval = new SimpleDateFormat("yyyy-MM-dd").format(date);
                                }else {
                                    //普通数值类型直接转String
                                   cellval = cell.toString();
                                }
                                break;
                            case BLANK:
                                System.out.println("空白字符");
                                break;
                            case BOOLEAN://布尔类型
                                cellval = String.valueOf(cell.getBooleanCellValue());
                                System.out.println(cellval);
                                break;
                            case ERROR:
                                System.out.println("该单元格格式错误");
                                break;
                        }
                        System.out.println(cellval);
                    }
                }
            }
        }
        inputStream.close();
    }

ExcelUtil封装

封装一个工具类
在这里插入图片描述
思路:

1.目的是将表格中的数据转换成实体类

2.实体类中字段和表格中的每一列应该如何映射?

一个简单的方法:
在这里插入图片描述

如图,可以在创建表时,在第二行增加一列与数据的字段相对应,在读出第二行数据时,将其存入map中的key

我们可以将第二行隐藏 ,不影响表格数据,同时也能读出数据
在这里插入图片描述

import org.apache.poi.ss.usermodel.*;

import java.io.FileInputStream;
import java.lang.reflect.Field;
import java.text.NumberFormat;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * 解析Excel表格
 * 用户只需传入对应版本的WorkBook对象,需要匹配版本 文件的输入流 对应实体类class
 * 得到解析表格以后的结果同时通过传入的实体类类型集合方式返回
 */
public class ExcelUtil {
    public static <T> List<T>  readExcel(Workbook workbook, FileInputStream inputStream ,Class<T> clazz)throws Exception{
        //给用户返回的实体类集合
        List<T> result = new ArrayList<>();
        //在工作簿中获取目标工作表

        //如果有多张工作表则遍历(目前默认只有一张表)
        Sheet sheet = workbook.getSheetAt(0);
        //获取工作表中的行数
        int rowNum = sheet.getPhysicalNumberOfRows();
        //拿到第一行被隐藏的数据
        Row row = sheet.getRow(1);
        //遍历第一行数据,遍历出的数据就是当前实体类对应的所有属性,把数据放入map中的key
        List<String> key = new ArrayList<>();
        //开始遍历
        for (Cell cell: row) {
            if(cell!=null){
                String value = cell.getStringCellValue();
                key.add(value);
                System.out.println(value);
            }
        }

        //从第三行开始遍历,以下就是正式数据
        for (int i = 2; i < rowNum; i++) {
            //获取属性以下的数据
            row = sheet.getRow(i);
            if(row!=null){
                //计数器 j 用于映射数据使用
                int j = 0;
                //用于保存数据的Map,在Map中建立 属性
                Map<String,String> excelMap = new HashMap<>();
                for (Cell cell:row){
                    if(cell!=null){
                        //把所有单元格中的数据设置为String
                        String value = getCellValue(cell);
                        if(value!=null && !value.equals("")){
                            //将每个单元格的数据储存到集合中
                            excelMap.put(key.get(j),value);
                            j++;
                        }
                    }
                }
                //map 转换成为实体类 创建对应实体类类型,把读取到的数据转换为实体类对象
                T t = mapToEntity(excelMap,clazz);
                result.add(t);
            }
        }
        inputStream.close();
        return result;

    }

    public static NumberFormat nf = NumberFormat.getNumberInstance();
    static{
        nf.setGroupingUsed(false);
    }

    //用于将单元格格式转换为String的方法
    public static String getCellValue(Cell cell){
        String cellval="";
        if(cell!=null){
            //每个单元格里的数据类型不同
//                        String val = cell.getStringCellValue();//会出问题
            //拿到数据先判断数据类型
            //所有读取到的数据的类型

            CellType cellType = cell.getCellType();
            //读取出的单元格数据最后都会存成String数据

            //根据不同的类型读取数据
            switch (cellType){
                case STRING://字符串
                    cellval = cell.getStringCellValue();
//                                System.out.println(cellval);
                    break;
                case NUMERIC://数值类型
                    //判断是否为日期
                    if(DateUtil.isCellDateFormatted(cell)){
//                                    System.out.println("当前单元格是日期类型");
                        Date date = cell.getDateCellValue();
                        cellval = new SimpleDateFormat("yyyy-MM-dd").format(date);
                    }else {
                        //普通数值类型直接转String
                        cellval = nf.format(cell.getNumericCellValue());
                    }
                    break;
                case BLANK:
//                    System.out.println("空白字符");
                    break;
                case BOOLEAN://布尔类型
                    cellval = String.valueOf(cell.getBooleanCellValue());
                    System.out.println(cellval);
                    break;
                case ERROR:
//                    System.out.println("该单元格格式错误");
                    break;
            }
            System.out.println(cellval);
        }
        return cellval;
    }

    private static <T> T mapToEntity(Map<String,String> map,Class<T> entity) throws InstantiationException, IllegalAccessException {
        T t = null;
        try{
            t = entity.newInstance();
            for(Field field:entity.getDeclaredFields()){
                //map中是否包含拿到的属性名称
                if(map.containsKey(field.getName())){
                    boolean flag = field.isAccessible();
                    field.setAccessible(true);
                    //获取Map中的属性对应的值
                   String str =  map.get(field.getName()); //key值
                    //获取实体类属性的类型
                    String type = field.getGenericType().toString();
                    //重新指定对应属性的值
                    if(str != null){
                        if(type.equals("class java.lang.String")){
                            field.set(t,str);
                        }else if(type.equals("class java.lang.Double")){
                            field.set(t,Double.parseDouble(String.valueOf(str)));
                        } else if (type.equals("class java.lang.Integer")) {
                            field.set(t,Integer.parseInt(String.valueOf(str)));
                        } else if (type.equals("class java.util.Date")) {
                            Date date = new SimpleDateFormat("yyyy-MM-dd").parse(str);
                            field.set(t,date);
                        }
                    }
                    field.setAccessible(flag);
                }
            }
            return t;
        }catch (Exception e){
            e.printStackTrace();
        }
    return t;
    }

}

CellType定义了不同的枚举类型,来作为表格数据的接收类型

  • _NONE 未知类型
  • NUMERIC 数值类型 包含整数、小数、日期!
  • STRING 字符串
  • FORMULA 公式
  • BLANK 空字符串 没有值,但是有单元格样式
  • BOOLEAN 布尔值
  • ERROR 错误单元格

所以为了统一将拿出来的数据用String 来接收,在取数据时需要做一个数据转换。

测试

新建一个接收的实体类ExcelUser

@Data
public class ExcelUser {
    private String userName;
    private Integer sex;
    private String phone;

}

public class ExcelReadTest {
    public static void main(String[] args) {
        try{
            new ExcelReadTest().getEntity();
        }catch (Exception e){
            e.printStackTrace();;
        }
    }
   
    public void getEntity() throws Exception {
        FileInputStream fileInputStream = new FileInputStream("D:\\myfiles\\临时文件\\用户07.xlsx");
        Workbook workbook = new XSSFWorkbook(fileInputStream);
        List<ExcelUser> list = ExcelUtil.readExcel(workbook,fileInputStream, ExcelUser.class);
        System.out.println(list);
    }

}

在这里插入图片描述

  • 9
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值