Excel操作-Apache-POI

news2024/12/26 21:21:48

Excel操作-Apache-POI

一、场景需求

  1. 将一些数据库信息导出为Excel表格

  1. 将Excel表格数据导入数据库

  1. 大量的数据的导入导出操作

解决方案

常用的解决方案:Apache POI与阿里巴巴easyExcel

Apache-POI介绍

Apache-POI是基于office open XML标准(OOXML)和Microsoft和OLE2复合文档格式(OLE2)处理各种格式的开源项目,简而言之,您可以使用java读写MS excel文件,可以使用java读写MS word 和MS powerPoint文件

模块

  • HSSF: 提供读写Microsoft Excel XLS格式(2003)档案的功能

  • XSSF: 提供读写Microsoft Excel OOXML XLSX(2007+)格式档案的功能

  • SXSSF: 提供低内存占用量读写Microsoft Excel OOXML XLSX格式档案的功能

  • HWPF: 提供读写Microsoft Word DOC97(2003)格式档案的功能

  • XWPF: 提供读写Microsoft Word Doc2003格式(2007+)档案的功能

  • HSLF/XSLF: 提供读写Misrosoft powerPoint格式档案的功能

  • HDGF/XDGF: 提供读Miscrosoft Visio格式档案的功能

  • HSMF: 提供Misrosoft outlook格式档案的功能

POI存在的问题

java解析、生成EExcel比较有名的框架有POI、JXL。但是他们都存在一个严重的问题,就是非常耗内存,也就是数量量比较大的时候可能会出现OOM(内存溢出)的问题,但是POI有一套SAX模式可以一定程度上解决一些内存溢出的问题,但是依旧没有完全解决内存消耗过大的问题

Excel表格03和07版本区别

POI可以支持不同版本的Excel表格的操作,那么现在大提升的就是03版本和07+版本,那么这两个表格有什么区别如下:

  • 03版本:最多行数支持65536,并且后缀为.xls

  • 07+版本:理论上是没有限制的,但是实际支持的行数为:1048576,并且后缀名为xlsx

POI操作Excel

首先第一件事情,我们需要创建项目导入对应的maven,这里我们需要演示03版本和07+版本,所以两个依赖都导入

  <!-- 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写入Excel表格数据的操作,那么我们首先要知道写一个表格需要的步骤

  1. 创建工作簿:Workbook

  1. 创建工作表:sheet

  1. 创建行:Row

  1. 创建列(单元格):Cell

  1. 具体数据写入

03版本测试

package ExcelTest;

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 java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

/**
 * @ClaseName: ExcelWrite03$
 * @Description:
 * @Author: wuhs
 * @Date: 2022/12/12$ 13:49$
 * @param: $
 * @return: $
 */
public class ExcelWrite03 {
    public static void main(String[] args) {
//        03版本的写入
//        创建工作簿
        Workbook workbook = new HSSFWorkbook();
//        创建工作表
        Sheet sheet = workbook.createSheet("03版本写入测试");
//        创建行(第一行)
        Row row = sheet.createRow(0);
//        创建单元格
        Cell cell11 = row.createCell(0);
//        写入数据
        cell11.setCellValue("商品ID");

//        写入数据(1,2)
        Cell cell = row.createCell(1);
        cell.setCellValue("商品名称");

//        第二行
        Row row1 = sheet.createRow(1);//创建行
        Cell cell21 = row1.createCell(0);//创建单元格
        cell21.setCellValue(1);
        Cell cell22 = row1.createCell(1);
        cell22.setCellValue("电脑");

//        生成一张表-IO流
        FileOutputStream fileOutputStream = null;
        try {
            fileOutputStream = new FileOutputStream("./03版本写入测试.xls");
            workbook.write(fileOutputStream);
            try {
//                关闭流
                fileOutputStream.close();
                System.out.println("表格生成完毕");
            } catch (IOException e) {
                e.printStackTrace();
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }
}

运行结果

07版本测试

package ExcelTest;

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.streaming.SXSSFWorkbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

/**
 * @ClaseName: ExcelWrite03$
 * @Description:
 * @Author: wuhs
 * @Date: 2022/12/12$ 13:49$
 * @param: $
 * @return: $
 */
public class ExcelWrite07 {
    public static void main(String[] args) {
//        03版本的写入
//        创建工作簿
        Workbook workbook = new XSSFWorkbook();
//        创建工作表
        Sheet sheet = workbook.createSheet("07版本写入测试");
//        创建行(第一行)
        Row row = sheet.createRow(0);
//        创建单元格
        Cell cell11 = row.createCell(0);
//        写入数据
        cell11.setCellValue("商品ID");

//        写入数据(1,2)
        Cell cell = row.createCell(1);
        cell.setCellValue("商品名称");

//        第二行
        Row row1 = sheet.createRow(1);//创建行
        Cell cell21 = row1.createCell(0);//创建单元格
        cell21.setCellValue(1);
        Cell cell22 = row1.createCell(1);
        cell22.setCellValue("电脑");

//        生成一张表-IO流
        FileOutputStream fileOutputStream = null;
        try {
            fileOutputStream = new FileOutputStream("./07版本写入测试.xlsx");
            workbook.write(fileOutputStream);
            try {
//                关闭流
                fileOutputStream.close();
                System.out.println("表格生成完毕");
            } catch (IOException e) {
                e.printStackTrace();
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }
}

运行结果

批量数据写入

那么不管使用03还是07+版本,那么在实际开发中都会碰到批量写入数据的操作,尤其是数据量比较多的时候,那么03版本和07+版本是处理的方式是不同的:

03版本HSSF:

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

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

07版本XSSF:

  • 数据写入速度比较慢,因为XSSF是获取全部行的数据,因此会消耗大量的内存,数据量庞大的时候有可能会发现内存溢出

  • 可以写入较大的数据,比如10W+数据

具体测试

03版本HSSF

package ExcelTest;

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 java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

/**
 * @ClaseName: WriteBatchDate03$
 * @Description:
 * @Author: wuhs
 * @Date: 2022/12/12$ 14:48$
 * @param: $
 * @return: $
 */
public class WriteBatchDate03 {//03版本批量写入

    public static void main(String[] args) {
        //    开始时间
        long start = System.currentTimeMillis();
//    创建工作簿
        Workbook workbook = new HSSFWorkbook();
//    创建表
        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);
            }
        }
        try {
            FileOutputStream fileOutputStream = new FileOutputStream("./WriteBatchDate03.xls");
            try {
                workbook.write(fileOutputStream);
                fileOutputStream.close();
                System.out.println("表格生成完毕!!!!");
            } catch (IOException e) {
                e.printStackTrace();
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
//    结束时间
        long end = System.currentTimeMillis();
        System.out.println("执行时间:"+(end-start)/1000+"s");
    }

}

07版本HSSF

package ExcelTest;

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 java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

/**
 * @ClaseName: WriteBatchDate03$
 * @Description:
 * @Author: wuhs
 * @Date: 2022/12/12$ 14:48$
 * @param: $
 * @return: $
 */
public class WriteBatchDate07 {//07版本批量写入

    public static void main(String[] args) {
        //    开始时间
        long start = System.currentTimeMillis();
//    创建工作簿
        Workbook workbook = new XSSFWorkbook();
//    创建表
        Sheet sheet = workbook.createSheet("07版本批量写入");
//    创建数据
        for (int rowNum = 0; rowNum < 65537; rowNum++) {//行
            Row row = sheet.createRow(rowNum);
            for (int cellNum = 0; cellNum < 20; cellNum++) {//列
                Cell cell = row.createCell(cellNum);
                cell.setCellValue(cellNum + 1);
            }
        }
        try {
            FileOutputStream fileOutputStream = new FileOutputStream("./WriteBatchDate07.xlsx");
            try {
                workbook.write(fileOutputStream);
                fileOutputStream.close();
                System.out.println("表格生成完毕!!!!");
            } catch (IOException e) {
                e.printStackTrace();
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
//    结束时间
        long end = System.currentTimeMillis();
        System.out.println("执行时间:"+(end-start)/1000+"s");
    }

}

大数据量写入

大数据写SXSSF

  • 为了解决大数据量写入的问题,所以官方提供了SXSSF来解决大文件写入问题,所以它可以写入非常大量的数据,比如上百万条的数据,并且写入速度更快,占用内存更少

  • 官方文档翻译:SXSSF在兼容XSSF的同时,能够应对大数据量和内存空间有限的情况,SXSSF每次获取的行数是在一个数值范围内,这个范围称为“滑动窗口”,在这个窗口内的数据均存在于内存中,超过这个窗口大小时,数据会被写入磁盘,由此控制内存使用,相比较而言,XSSF则每次获取全部行

  • 窗口默认大小为100(可以自定义),如果行数为101行,那么此时超过了窗口限制,索引值最小的行会被“刷入”磁盘,需要注意,SXSSF会自动分配临时文件,这些临时文件需要我们手动清除,清除的方式是使用dispose()方法;

  • 自定义的方式:SXSSFWorkBook wb = new SXSSFWorkbook(自定义窗口大小)

package ExcelTest;

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.streaming.SXSSFWorkbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

/**
 * @ClaseName: WriteBigDate$
 * @Description:
 * @Author: wuhs
 * @Date: 2022/12/12$ 15:15$
 * @param: $
 * @return: $
 */
public class WriteBigDate {
    public static void main(String[] args) {
        //    开始时间
        long start = System.currentTimeMillis();
//    创建工作簿
        Workbook workbook = new SXSSFWorkbook();
//    创建表
        Sheet sheet = workbook.createSheet("07版本大数量写入");
//    创建数据
        for (int rowNum = 0; rowNum < 100000; rowNum++) {//行
            Row row = sheet.createRow(rowNum);
            for (int cellNum = 0; cellNum < 20; cellNum++) {//列
                Cell cell = row.createCell(cellNum);
                cell.setCellValue(cellNum + 1);
            }
        }
        try {
            FileOutputStream fileOutputStream = new FileOutputStream("./WriteBigDate07.xlsx");
            try {
                workbook.write(fileOutputStream);
                fileOutputStream.close();
                System.out.println("表格生成完毕!!!!");
            } catch (IOException e) {
                e.printStackTrace();
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
//    结束时间
        long end = System.currentTimeMillis();
        System.out.println("执行时间:"+(end-start)/1000+"s");
    }
}

POI操作--读取Excel

其实读取用到的对象和写入的对象是一样的:

  • 工作簿:WorkBoot(所有的Excel工作簿的操作都是通过此类型进行设置)

03 HSSF

07 XSSF

  • 数据行:Sheet(所有关于数据表的操作都是通过sheet都可以进行设置)

  • 行:ROW

  • 单元格:Cell

具体读取步骤

利用文件流来读取

  1. 获取工作簿

  1. 获取表

  1. 获取行

  1. 获取单元格

  1. 读取数据

那么这里我们还是针对03和07版本的读取进行测试

03版本读取

package ExcelTest;

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 java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

/**
 * @ClaseName: ReadExcel03$
 * @Description:
 * @Author: wuhs
 * @Date: 2022/12/12$ 15:25$
 * @param: $
 * @return: $
 */
public class ReadExcel03 {
    public static void main(String[] args) {
        try {
//            通过文件流读取
            FileInputStream inputStream = new FileInputStream("./03版本写入测试.xls");
//            读取工作簿
            Workbook workbook = new HSSFWorkbook(inputStream);
//            获取表(通过下标方式来进行读取或者可以采用表名进行读取)
            Sheet sheet = workbook.getSheetAt(0);
//            获取行(采用下标方式获取)
            Row row = sheet.getRow(0);
//            获取单元格(采用下标方式获取)
            Cell cell = row.getCell(0);
//            读取数据
            String stringCellValue = cell.getStringCellValue();
            System.out.println(stringCellValue);
            inputStream.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

07版本读取

package ExcelTest;

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 java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

/**
 * @ClaseName: ReadExcel03$
 * @Description:
 * @Author: wuhs
 * @Date: 2022/12/12$ 15:25$
 * @param: $
 * @return: $
 */
public class ReadExcel07 {
    public static void main(String[] args) {
        try {
//            通过文件流读取
            FileInputStream inputStream = new FileInputStream("./07版本写入测试.xlsx");
//            读取工作簿
            Workbook workbook = new XSSFWorkbook(inputStream);
//            获取表(通过下标方式来进行读取或者可以采用表名进行读取)
            Sheet sheet = workbook.getSheetAt(0);
//            获取行(采用下标方式获取)
            Row row = sheet.getRow(0);
//            获取单元格(采用下标方式获取)
            Cell cell = row.getCell(0);
//            读取数据
            String stringCellValue = cell.getStringCellValue();
            System.out.println(stringCellValue);
            inputStream.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

批量读取

在我们实际的开发过程中,经常会遇到不同类型的数据,这也是我们工作中最麻烦的操作,首先我们假设有一张表:商品表.xls,具体数据如下:

那么我们想要读取这张表,需要首先读取标题部分,然后在读取具体的数据部分,这两不一定要分开的

读取标题部分

package ExcelTest;

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 java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

/**
 * @ClaseName: ReadExcel03$
 * @Description:
 * @Author: wuhs
 * @Date: 2022/12/12$ 15:25$
 * @param: $
 * @return: $
 */
public class ReadExcelCellType {
    public static void main(String[] args) {
        try {
//            通过文件流读取
            FileInputStream inputStream = new FileInputStream("./商品表.xls");
//            读取工作簿
            Workbook workbook = new HSSFWorkbook(inputStream);
//            获取表(通过下标方式来进行读取或者可以采用表名进行读取)
            Sheet sheet = workbook.getSheetAt(0);
//            获取表中的第一行数据
            Row titleRow = sheet.getRow(0);
            if (titleRow!=null){
//                获取标题单元格的数量,用于表里获取所有单元格
                int cellNum = titleRow.getPhysicalNumberOfCells();
                System.out.println(cellNum);
                for (int i = 0; i <cellNum ; i++) {
//                    获取所有单元格
                    Cell cell = titleRow.getCell(i);
                    if (cell!=null){
//                        获取单元格的数据
                        String stringCellValue = cell.getStringCellValue();
                        System.out.print(stringCellValue+"\t");
                    }
                    
                }
            }


            inputStream.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

.getCellType()的几种类型值

CELL_TYPE_NUMERIC

数值型

CELL_TYPE_STRING

字符串型

CELL_TYPE_FORMULA

公式型

CELL_TYPE_BLANK

空值

CELL_TYPE_BOOLEAN

布尔型

CELL_TYPE_ERROR

错误

读取数据部分

package ExcelTest;

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

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * @ClaseName: ReadExcel03$
 * @Description:
 * @Author: wuhs
 * @Date: 2022/12/12$ 15:25$
 * @param: $
 * @return: $
 */
public class ReadExcelCellType {
    public static void main(String[] args) throws IOException {

//            通过文件流读取
        FileInputStream inputStream = new FileInputStream("./商品表.xls");
//            读取工作簿
        Workbook workbook = new HSSFWorkbook(inputStream);
//            获取表(通过下标方式来进行读取或者可以采用表名进行读取)
        Sheet sheet = workbook.getSheetAt(0);
        //获取标题以下的具体内容
        //获取一共有多少行数据
        int rowNum = sheet.getPhysicalNumberOfRows();
//            跳过第一行标题数据获取以下具体内容
        for (int i = 1; i < rowNum; i++) {
            Row row = sheet.getRow(i);
            if (row != null) {
//                    获取每一行里面有多少单元格
                int NumberOfCells = row.getPhysicalNumberOfCells();
                for (int j = 0; j < NumberOfCells; j++) {
                    Cell cell = row.getCell(j);
                    if (cell != null) {
//                    获取所有读取数据的类型
                        CellType cellType = cell.getCellType();
//                            根据不同的数据类型来读取数据
                        String cellVal = "";
                        switch (cellType) {
                            case STRING://字符串
                                cellVal = cell.getStringCellValue();
                                break;
                            case NUMERIC://数值类型--日期也会当做数值类型
//                                    判断是否为日期
                                if (DateUtil.isCellDateFormatted(cell)) {
                                    Date date = cell.getDateCellValue();
                                    cellVal = new SimpleDateFormat("yyyy-MM-dd").format(date);
                                } else {
                                    cellVal = cell.toString();
                                }
                                break;
                        }
                        System.out.print(cellVal + "\t");
                    }

                }
                System.out.println();
            }
        }
        inputStream.close();

    }


}

完整代码

package ExcelTest;

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

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * @ClaseName: ReadExcel03$
 * @Description:
 * @Author: wuhs
 * @Date: 2022/12/12$ 15:25$
 * @param: $
 * @return: $
 */
public class ReadExcelCellType {
    public static void main(String[] args) throws IOException {

//            通过文件流读取
        FileInputStream inputStream = new FileInputStream("./商品表.xls");
//            读取工作簿
        Workbook workbook = new HSSFWorkbook(inputStream);
//            获取表(通过下标方式来进行读取或者可以采用表名进行读取)
        Sheet sheet = workbook.getSheetAt(0);
//            获取表中的第一行数据
        Row titleRow = sheet.getRow(0);
        if (titleRow != null) {
//                获取标题单元格的数量,用于表里获取所有单元格
            int cellNum = titleRow.getPhysicalNumberOfCells();
            for (int i = 0; i < cellNum; i++) {
//                    获取所有单元格
                Cell cell = titleRow.getCell(i);
                if (cell != null) {
//                        获取单元格的数据
                    String stringCellValue = cell.getStringCellValue();
                    System.out.print(stringCellValue + "\t");
                }
            }
        }
        System.out.println();
        //获取标题以下的具体内容
        //获取一共有多少行数据
        int rowNum = sheet.getPhysicalNumberOfRows();
//            跳过第一行标题数据获取以下具体内容
        for (int i = 1; i < rowNum; i++) {
            Row row = sheet.getRow(i);
            if (row != null) {
//                    获取每一行里面有多少单元格
                int NumberOfCells = row.getPhysicalNumberOfCells();
                for (int j = 0; j < NumberOfCells; j++) {
                    Cell cell = row.getCell(j);
                    if (cell != null) {
//                    获取所有读取数据的类型
                        CellType cellType = cell.getCellType();
//                            根据不同的数据类型来读取数据
                        String cellVal = "";
                        switch (cellType) {
                            case STRING://字符串
                                cellVal = cell.getStringCellValue();
                                break;
                            case NUMERIC://数值类型--日期也会当做数值类型
//                                    判断是否为日期
                                if (DateUtil.isCellDateFormatted(cell)) {
                                    Date date = cell.getDateCellValue();
                                    cellVal = new SimpleDateFormat("yyyy-MM-dd").format(date);
                                } else {
                                    cellVal = cell.toString();
                                }
                                break;
                        }
                        System.out.print(cellVal + "\t");
                    }

                }
                System.out.println();
            }
        }
        inputStream.close();

    }


}

测试结果

ExcelUtil封装

在我们给用户的excel文件的模板中隐藏一行“实体类名”,以便我们在读取文件的时候获取,并作为key值存储

封装的工具类需求:用户只需要传入Workbook对象(匹配版本),文件输入流,对应实体的class。就可以得到接卸表哥以后的结果 ,同时通过传入的实体类类型集合的方式返回

package Util;

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.*;

/**
 * @ClaseName: ExcelUtil$
 * @Description:用户只需要传入Workbook对象(匹配版本),文件输入流,对应实体的class。就可以得到接卸表哥以后的结果 ,同时通过传入的实体类类型集合的方式返回* *
 * @Author: wuhs
 * @Date: 2022/12/12$ 16:34$
 * @param: $
 * @return: $
 */
public class ExcelUtil {
    public static <T> List<T> readExcel(Workbook workbook, FileInputStream inputStream, Class<T> T) throws Exception {
//        给用户返回的实体类集合
        List<T> result = new ArrayList<>();
//        在工作簿中获取工作表
        Sheet sheet = workbook.getSheetAt(0);
//        获取工作表中的行数(第一行是标题、第二行是对应的实体)
        int NumberOfRows = 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);
            }
        }
        for (int i = 2; i < NumberOfRows; 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 celVale = getCelVale(cell);
                        if (celVale != null && !celVale.equals("")) {
                            excelMap.put(key.get(j), celVale);
                            j++;
                        }
                    }
                }
                System.out.println();
//                创建对应实体类类型,并且把读取到的数据转换成实体类对象
                T t = mapToEntity(excelMap,T);
                result.add(t);
            }
        }
        inputStream.close();
        return result;
    }

    public static NumberFormat nf = NumberFormat.getInstance();

    static {
        nf.setGroupingUsed(false);

    }

    public static String getCelVale(Cell cell) {
        CellType cellType = cell.getCellType();
        String valueCel = "";
        switch (cellType) {
            case STRING://字符串
                valueCel = cell.getStringCellValue();
                break;
            case NUMERIC://数值类型--日期也会当做数值类型
//                                    判断是否为日期
                if (DateUtil.isCellDateFormatted(cell)) {
                    Date date = cell.getDateCellValue();
                    valueCel = new SimpleDateFormat("yyyy-MM-dd").format(date);
                } else {
                    valueCel = nf.format(cell.getNumericCellValue());
                }
                break;
            case BLANK://空字符串
                break;
            case BOOLEAN://布尔类型
                valueCel = String.valueOf(cell.getBooleanCellValue());
                break;
            case ERROR:
                break;
        }
        System.out.print(valueCel + "\t");
        return valueCel;
    }


    private static <T> T mapToEntity(Map<String, String> map, Class<T> entity) throws Exception, IllegalAccessException {
        T t = null;
        t = entity.newInstance();//基于反射拿到实体类属性
        for (Field field : entity.getDeclaredFields()) {
            if (map.containsKey(field.getName())) {//属性的名称map中是否包含
                boolean flag = field.isAccessible();
//                值为 true 则指示反射的对象在使用时应该取消 Java 语言访问检查。
//                值为 false 则指示反射的对象应该实施 Java 语言访问检查。
                field.setAccessible(true);
//                根据key拿到value值
                String mapValue = map.get(field.getName());
//                获取实体类属性的类型
                String type = field.getGenericType().toString();
                System.out.println("type------------"+type);
                //重新制定对应的属性的值
                if (mapValue != null) {
                    if (type.equals("class java.lang.String")) {
//                        Field.set()向对象的这个Field属性设置新值value
                        field.set(t, mapValue);
                    } else if (type.equals("double")) {
                        field.set(t, Double.parseDouble(String.valueOf(mapValue)));
                    } else if (type.equals("int")) {
                        field.set(t, Integer.parseInt(String.valueOf(mapValue)));
                    } else if (type.equals("class java.util.Date")) {
                        Date date = new SimpleDateFormat("yyyy-MM-dd").parse(mapValue);
                        field.set(t, date);
                    }

                }
                field.setAccessible(flag);
            }

        }
        return t;
    }

}

测试:

package ExcelTest;

import Util.ExcelUtil;
import entity.Product;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Workbook;

import java.io.FileInputStream;
import java.util.List;

/**
 * @ClaseName: UtilTest$
 * @Description:
 * @Author: wuhs
 * @Date: 2022/12/12$ 16:50$
 * @param: $
 * @return: $
 */
public class UtilTest {
    public static void main(String[] args)throws Exception {
        FileInputStream fileInputStream = new FileInputStream("./商品表.xls");
        Workbook workbook = new HSSFWorkbook(fileInputStream);
        List<Product> products = ExcelUtil.readExcel(workbook, fileInputStream, Product.class);
        System.out.println(products);

    }
}

结果:

Excel中的公式---POI读取公式

excel表中会有一些公式(求和、求差等),所以如果我们想要获取公式的内容,那么我们需要在读取的时候用到POI提供的一些公式对象,当然如果是想直接读取数值的话,可以直接通过数据类型来读取

package ExcelTest;

import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFFormulaEvaluator;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

import java.io.FileInputStream;

/**
 * @ClaseName: GetFormula$
 * @Description:
 * @Author: wuhs
 * @Date: 2022/12/12$ 18:40$
 * @param: $
 * @return: $
 */
public class GetFormula {
    public static void main(String[] args) throws Exception {
        //    通过文件流读取excel工作簿
        FileInputStream fileInputStream = new FileInputStream("./读取公式.xlsx");
//        获取工作簿
        Workbook workbook = new XSSFWorkbook(fileInputStream);
//        获取表(通过下标的方式来获取)
        Sheet sheet = workbook.getSheetAt(0);
//        获取行和单元格(带公式)
        Row row = sheet.getRow(2);
        Cell cell = row.getCell(0);
        也可以直接读取数值
//        System.out.println(cell.getNumericCellValue());
        FormulaEvaluator formulaEvaluator = new XSSFFormulaEvaluator((XSSFWorkbook) workbook);
//        获取单元格的内容
        CellType cellType = cell.getCellType();
        switch (cellType){
            case FORMULA://公式
//                获取公式
                String formula = cell.getCellFormula();
                System.out.println(formula);
                CellValue value = formulaEvaluator.evaluate(cell);
                String val = value.formatAsString();
                System.out.println(val);
                break;

        }
        fileInputStream.close();
    }

}

执行结果

Excel操作-EasyExcel

EasyExcel是一个基于Java的简单、省内存的读写Excel的开源项目,在尽可能节约内存的情况下支持读写百M的Excel。具有快速、简单避免OOM的java处理的Excel工具

具体操作:

注意:这个工具的操作都是依赖于实体类,所以我们需要建立一个实体类

package entity;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;

import java.util.Date;

/**
 * @ClaseName: Product$
 * @Description:
 * @Author: wuhs
 * @Date: 2022/12/12$ 16:57$
 * @param: $
 * @return: $
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class Product {
    private int id;
    private String name;
    private double price;
    private int count;
    private Date createTime;
}

写入操作:

直接参照官方文档即可,里面写的很详细;https://easyexcel.opensource.alibaba.com/

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/358785.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

合同审核机器人,提高合同审核效率,规避企业损失

官方网址&#xff1a;www.hanyunintel.com 在合同审核中面临的问题和挑战&#xff1a; 一、人工合同审核风险高&#xff1a; 人工合同审核依赖审核人员业务素养、精神状态、工作量、体力、工作态度及责任心&#xff0c;难免存在人工审核合同不严谨的情况&#xff0c;如果不能及…

【C++提高编程】C++全栈体系(二十一)

C提高编程 第三章 STL - 常用容器 三、deque容器 1. deque容器基本概念 功能&#xff1a; 双端数组&#xff0c;可以对头端进行插入删除操作 deque与vector区别&#xff1a; vector对于头部的插入删除效率低&#xff0c;数据量越大&#xff0c;效率越低deque相对而言&am…

简洁易用的记账小程序——微点记账

背景 由于每个月的信用卡账单太过吓人&#xff0c;记性也不是特别的好&#xff0c;加上微信支付宝账单中有些明细不是很明确。比如在京东花销的明细不会记录用户购买了什么&#xff0c;只会记录那个通道支出的。所以&#xff0c;才会有了想自己开发一款记账小程序&#xff0c;…

面试常问-Alpha测试和Beta测试

Alpha测试 Alpha测试是一种验收测试&#xff0c;在识别典型用户可能执行的任务并对其进行测试之前&#xff0c;执行该测试是为了识别所有可能的问题和错误。 尽可能简单地说&#xff0c;这种测试之所以被称为alpha&#xff0c;只是因为它是在软件开发的早期、接近开发结束时和…

使用loading动画让你的条件渲染页面更高级

前言在我们做项目的使用常常会使用条件渲染去有选择的给用户展示相关页面&#xff0c;如果渲染的数据或场景比较多比较复杂&#xff0c;那么往往需要3、4s的时间去完成&#xff0c;用户点击了之后就会陷入3、4s的空白期&#xff0c;并且这段时间屏幕是处于一种”未响应“的状态…

【欧拉筛法】洛谷 P3383 线性筛素数

3383. 线性筛素数 文章目录题目描述输入格式&#xff1a;输出格式&#xff1a;数据范围输入样例输出样例方法一&#xff1a;埃氏筛法解题思路代码复杂度分析&#xff1a;方法二&#xff1a;欧拉筛法解题思路代码复杂度分析&#xff1a;两种方法对比埃氏筛法欧拉筛法题目描述 给…

Jetpack管理生命周期——Lifecycle

Android Jetpack 对于任何一个产品来说&#xff0c;我们开发中都会面对哪些问题&#xff1f;如&#xff1a;产品交互、用户体验、代码结构、数据获取、数据存储、网络优化、任务调度等等&#xff0c;虽然在现在的阶段这些问题已经有了很好的解决和优化&#xff0c;也有很多大神…

堆球问题,开普勒猜想(格密码相关)

目录 一. 介绍 二. 历史进展分析 三.2维下的堆球问题 四. 3维下的堆球问题 五. 8维与24维下的堆球问题 总结 一. 介绍 堆球问题又叫堆球理论、最密堆积、球填充&#xff0c;英文为The Theory Of Sphere Packings。 堆球问题的本质就是填充一堆大小相同的球。要求这些球…

FANUC机器人通过KAREL程序实现与PLC位置坐标通信的具体方法示例

FANUC机器人通过KAREL程序实现与PLC位置坐标通信的具体方法示例 在通信IO点位数量足够的情况下,可以使用机器人的IO点传输位置数据,这里以传输机器人的实时位置为例进行说明。 基本流程如下图所示: 基本步骤可参考如下: 首先确认机器人控制柜已经安装了总线通信软件(例如…

CMMI高效实施的5大注意事项

1、明确CMMI实施目标及范围 CMMI在实施过程中&#xff0c;需要根据组织的实际情况&#xff0c;确定CMMI的实施目标和范围&#xff0c;制定实施的计划表&#xff0c;为后续组织过程改进提供明确的方向。 CMMI高效实施注意事项&#xff1a;实施目标及范围的确定​ 2、建立…

虹科新闻 | 虹科与b-plus正式建立合作伙伴关系,共同致力于用于ADAS/AD系统开发的VV测量解决方案

虹科b-plus 携手共创未来&#xff01; 近期&#xff0c;虹科与德国b-plus正式建立合作伙伴关系。未来&#xff0c;虹科与b-plus将共同致力于提供用于ADAS/AD系统开发的V&V测量解决方案。 合作寄语 虹科CEO陈秋苑女士表示&#xff1a;“虹科非常期待与b-plus合作&#x…

线上研讨会报名 | 与龙智、Perforce共探大规模研发中的数字资产管理与版本控制,赢取千元大奖

2023年2月28日下午2:00&#xff0c;加入全球领先的数字资产管理工具厂商Perforce联合中国授权合作伙伴龙智举办的Perforce on Tour网络研讨会&#xff0c;除了与游戏、芯片、虚拟制作行业专家探讨并分享最佳实践外&#xff0c;还可以赢取惊喜大奖&#xff0c;包括千元华为手环、…

语言文件操作

&#x1f331;博客主页&#xff1a;大寄一场. &#x1f331;系列专栏&#xff1a;C语言学习笔记 &#x1f618;博客制作不易欢迎各位&#x1f44d;点赞⭐收藏➕关注 目录 前言 C语言中的文件打开和关闭 文件指针 文件的打开和关闭 fclose 文件的顺序读写 fseek ftell …

Flink-多流转换(Union、Connect、Join)

文章目录多流转换分流基本合流操作联合&#xff08;Union&#xff09;连接&#xff08;Connect&#xff09;基于时间的合流——双流联结&#xff08;Join&#xff09;窗口联结&#xff08;Window Join&#xff09;间隔联结&#xff08;Interval Join&#xff09;窗口同组联结&a…

【Vue3】组件数据懒加载

组件数据懒加载-基本使用 目标&#xff1a;通过useIntersectionObserver优化新鲜好物和人气推荐模块 电商类网站&#xff0c;尤其是首页&#xff0c;内容有好几屏&#xff0c;而如果一上来就加载所有屏的数据&#xff0c;并渲染所有屏的内容会导致首页加载很慢。 数据懒加载&a…

Java面试题--熔断和降级的区别

熔断和降级都是系统自我保护的一种机制&#xff0c;但二者又有所不同&#xff0c;它们的区别主要体现在以下几点&#xff1a; 概念不同 触发条件不同 归属关系不同 1.概念不同 1.1熔断概念 “熔断”一词早期来自股票市场。熔断&#xff08;Circuit Breaker&#xff09;也…

Python3-数据类型转换

有时候&#xff0c;我们需要对数据内置的类型进行转换&#xff0c;数据类型的转换&#xff0c;一般情况下你只需要将数据类型作为函数名即可。 Python 数据类型转换可以分为两种&#xff1a; 隐式类型转换 - 自动完成 显式类型转换 - 需要使用类型函数来转换 隐式类型转换 在…

一图说明 monorepo 落地流程方案

关于 monorepo 初次讨论已有2年载&#xff0c;目前团队已经沉淀了成熟的技术方案且经受住了实战考验。所以特梳理相关如下&#xff1a; 也算是关于之前发起的 monorepo–依赖 的解答篇。 上图为目前团队贡献的主流程&#xff1a;① 本地开发 > ② 提交Git仓库 > ③ 触发…

网络安全高级攻击

对分类器的高层次攻击可以分为以下三种类型&#xff1a;对抗性输入&#xff1a;这是专门设计的输入&#xff0c;旨在确保被误分类&#xff0c;以躲避检测。对抗性输入包含专门用来躲避防病毒程序的恶意文档和试图逃避垃圾邮件过滤器的电子邮件。数据中毒攻击&#xff1a;这涉及…