POI技术

一、什么是POI

Apache POI 是用java编写的免费开源的JAVA API,Apach POI提供了API给Java 程式对 Microsoft Office(Excel、WORD、PowerPoint、Visio 等,主要实现用于 Excel)格式档案读和写的功能,POI 为 “ Poor Obfuscation Implementation ” 的首字母缩写,意为“简洁版的模糊实现”。

简单来说就是使用java来对excel文件进行读写操作,POI提供了java操作excel等文件的API。

二、Apache POI的使用

  1. 导入相应依赖,因为excel有两个版本,一个是03版本.xls,一个是07版本.xlsx:
<!--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>

  1. 进行相关API测试,主要分为对Excel文件的读和写,全在代码里了
public class ExcelTest {

    // 记录下项目根路径,为方便后面拼接文件路径
    String Path = "D:\\IdeaProjects\\testdemo\\poi_demo\\";


    @Test //03版本,写入测试
    public void testWrite03() throws Exception{

        // 1. 创建工作簿(也就是excel文件)
        Workbook workbook = new HSSFWorkbook(); //03版本的excel,03版本最大支持65536行数据     xxx.xls
        //Workbook workbook = new XSSFWorkbook(); // 07版本的excel,07版本没有限制   xxx.xlsx

        // 2. 创建表(也就是excel文件中左下角的sheet表)
        Sheet sheet = workbook.createSheet("统计表01");

        // 3. 根据表,创建行
        Row row = sheet.createRow(0);

        // 4. 根据行,创建单元格  (其实就是坐标,注意一定是先有行,再有列。根据行创建唯一的单元格)
        Cell cell1 = row.createCell(0);
        cell1.setCellValue("球队");
        Cell cell2 = row.createCell(1);
        cell2.setCellValue("球员");

        // 重复上的操作,再创建一行
        Row row1 = sheet.createRow(1);
        Cell cell = row1.createCell(0);
        cell.setCellValue("太阳");
        Cell cell3 = row1.createCell(1);
        cell3.setCellValue("保罗");

        // 新建文件输出流
        FileOutputStream fos = new FileOutputStream(Path + "NBA统计表.xls");
        // 将设置好的工作簿,写入磁盘
        workbook.write(fos);

        fos.close();//关闭流

        System.out.println("文件生成成功!");


    }

    @Test  // 07版本,写入测试
    public void testWrite07BigData() throws Exception{

        long begin = System.currentTimeMillis();

        //Workbook workbook = new XSSFWorkbook();// 创建07版本的Excel
        Workbook workbook = new SXSSFWorkbook(); // 创建07版本的快速写入实现类

        Sheet sheet = workbook.createSheet();

        for (int i = 0; i < 100000 ; i++) { // 07版本行数无限制,所以创建个大于65536行的表

            Row row = sheet.createRow(i);
            for (int j = 0; j < 5; j++) { // 每行写5个数据
                Cell cell = row.createCell(j);
                cell.setCellValue(i+","+j);
            }

        }

        FileOutputStream fos = new FileOutputStream(Path+"观众数量统计表.xlsx");
        workbook.write(fos);
        fos.close();

        // 清除临时文件
        ((SXSSFWorkbook)workbook).dispose();

        long end = System.currentTimeMillis();

        // 经测试发现:
        // 普通07版本XSSFWorkbook,耗时50秒
        // 快速版本SXSSFWorkbook,耗时6秒,速度提升近10倍
        System.out.println("写入文件完成,共耗时:"+(end-begin)/1000+" 秒");
    }

    @Test // 测试 03、07版本  读
    public void testRead03() throws Exception{

        FileInputStream fis = new FileInputStream(Path+"NBA统计表.xls");

        // 读取工作簿
        Workbook workbook = new HSSFWorkbook(fis);
        //Workbook workbook = new XSSFWorkbook(fis);  07版本就实现类不同而已

        // 获取第一个表
        Sheet sheet = workbook.getSheetAt(0);

        // 获取第一行第一列的单元格
        Row row = sheet.getRow(0);
        Cell cell = row.getCell(0);

        // 输入单元格内容
        System.out.println(cell.getStringCellValue());

        fis.close();
    }

    @Test // 在工作中,一个excel中会有很多数据类型,所以在读的时候就要加各种判断类型的逻辑
    public void testCellType() throws Exception{

        InputStream is = new FileInputStream(Path+"会员消费商品明细表.xls");
        Workbook workbook = new HSSFWorkbook(is);

        Sheet sheet = workbook.getSheetAt(0);

        // 读取第一行表头数据
        Row row = sheet.getRow(0);
        if(row!=null){
            int cellCount = row.getPhysicalNumberOfCells();// 获取改行的长度

            for (int i = 0; i <cellCount ; i++) {
                Cell cell = row.getCell(i);
                if(cell!=null){
                    String cellValue = cell.getStringCellValue();// 因为第一行一般都是表头,所以直接用String类型来接收了
                    System.out.print(cellValue+"|");
                }
            }
            System.out.println();//换行
        }

        // 读取商品列表数据
        int rowCount = sheet.getPhysicalNumberOfRows();//获取该sheet有几行
        for (int i = 1; i <rowCount ; i++) { // 第一行已经获取过了,所以从第二行开始读取数据

            Row rowData = sheet.getRow(i);
            if(rowData!=null){

                int cellCount = rowData.getPhysicalNumberOfCells();//获取该行共有几列

                for (int j = 0; j < cellCount; j++) {

                    Cell cell = rowData.getCell(j);
                    if(cell!=null){

                        int cellType = cell.getCellType();//获取单元格数据类型

                        String cellValue = "";
                        switch (cellType) {

                            case HSSFCell.CELL_TYPE_STRING://字符串
								System.out.print("【STRING】");
								cellValue = cell.getStringCellValue();
								break;

							case HSSFCell.CELL_TYPE_BOOLEAN://布尔
								System.out.print("【BOOLEAN】");
								cellValue = String.valueOf(cell.getBooleanCellValue());
								break;

							case HSSFCell.CELL_TYPE_BLANK://空
								System.out.print("【BLANK】");
								break;

							case HSSFCell.CELL_TYPE_NUMERIC:
								System.out.print("【NUMERIC】");
								//cellValue = String.valueOf(cell.getNumericCellValue());

								if (HSSFDateUtil.isCellDateFormatted(cell)) {//日期
									System.out.print("【日期】");
									Date date = cell.getDateCellValue();
									cellValue = new DateTime(date).toString("yyyy-MM-dd");
								} else {
									// 不是日期格式,则防止当数字过长时以科学计数法显示
									System.out.print("【转换成字符串】");
									cell.setCellType(HSSFCell.CELL_TYPE_STRING);
									cellValue = cell.toString();
								}
								break;

							case Cell.CELL_TYPE_ERROR:
								System.out.print("【数据类型错误】");
								break;


                        }
							System.out.println(cellValue);

                    }
                }
            }
        }
       
        is.close();

    }
}

三、EasyExcel框架

EasyExcel是阿里巴巴开源的一个excel处理框架,以使用简单,节省内存著称,EasyExcel能大大减少占用内存的主要原因是在解析Excel时没有将文件数据一次性全部加载到内存中,而是从磁盘上一行行读取数据,逐个解析。
EasyExcel采用一行一行的解析模式,并将一行的解析结果以观察者的模式通知处理(AnalysisEventListener)。

下面是使用EasyExcel的代码示例:

  1. 创建一个实体类
@Data
public class BasketballPlayer {

    // @ExcelProperty 将实体类中字段和Excel中的标题互相绑定

    @ExcelProperty("球员")
    private String name;

    @ExcelProperty("数字")
    private Integer number;

    @ExcelProperty("球队")
    private String team;

    @ExcelProperty("生日")
    private Date Birthday;

    /**
     * ExcelIgnore 可以用来设置所要忽略的字段
     */
    @ExcelIgnore
    private String ignore;
}
  1. 创建针对此实体类的一个监听器
// 有个很重要的点 BasketballListener 不能被spring管理,要每次读取excel都要new,然后里面用到spring可以构造方法传进去
public class BasketballListener extends AnalysisEventListener<BasketballPlayer> {

    @Override
    public void invoke(BasketballPlayer basketballPlayer, AnalysisContext analysisContext) {
        System.out.println("====invoke方法执行,每读到一行数据就会走一次这个方法===");

        //这里的basketballPlayer就是excel中的数据,已经自动从excel中读取并封装到该实体类中了
        System.out.println(basketballPlayer.toString());

        System.out.println("这里可以进行一些逻辑处理,或者保存到数据库,等操作都可以进行");
    }

    @Override
    public void doAfterAllAnalysed(AnalysisContext analysisContext) {
        System.out.println("====整个sheet读完之后,doAfterAllAnalysed方法执行===");
    }
}
  1. 使用EasyExcel进行读写测试
public class EasyExcelTest {

    String path = "D:\\IdeaProjects\\testdemo\\poi_demo\\";

    @Test
    public void testRead(){

        String filePath = path +"NBA球员信息表.xlsx";

        EasyExcel.read(filePath, BasketballPlayer.class, new BasketballListener()).sheet().doRead();

    }

    @Test
    public void testWrite(){

        String filePath = path +"NBA统计表.xls";

        EasyExcel.write(filePath,BasketballPlayer.class).sheet("模板1").doWrite(data());

    }

    // 模拟要写入excel的数据
    private List<BasketballPlayer> data() {
        List<BasketballPlayer> list = new ArrayList<BasketballPlayer>();
        for (int i = 0; i < 10; i++) {
            BasketballPlayer data = new BasketballPlayer();
            data.setName("詹姆斯"+i+"号");
            data.setNumber(i);
            data.setTeam("球队"+i);
            data.setBirthday(new Date());
            list.add(data);
        }
        return list;
    }
}

从上面的示例中可以看到,使用EasyExcel几乎一行代码就可以完成对Excel的读写操作,而且他帮我们完成了excel数据封装到实体类这一步,且我们只需要在listener中写自己的业务逻辑即可。

不过上面的示例代码只是最简单的EasyExcel的使用,真正到开发中使用的话还需要根据具体的需求来具体应用。

EasyExcel官网上有具体的使用文档:EasyExcel官网地址

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值