Pol&&Excel

1、常用信息
  (1)、将用户信息导出为excel表格(导出数据…)

  (2)、将Excel表中的信息录入到网站数据库(习题上传…)

  (3)开发中经常会设计到excel的处理,如导出Excel,导入Excel到数据库中!操作Excel目前比较流行的就是Apache POI和阿里巴巴的easyExcel !

是什么:组件,工具

  Apache POI 是用Java编写的免费开源的跨平台的 Java API,Apache POI提供API给Java程式对Microsoft Office格式档案读和写的功能。

2、Apache POI
官网:

https://poi.apache.org/
1

3、easyExcel
官网:

https://www.yuque.com/easyexcel/doc/easyexcel
1
  Java领域解析,生成Excel比较有名的框架有Apache poi,jxl等,但他们都存在一个严重的问题就是非常的耗内存,如果你的系统并发量不大的话可能还行,但是一旦并发上来后一定会OOM或者JVM频繁的full gc.

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

  EasyExcel采用一行一行的解析模式,并将一行的解析结果以观察者的模式通知处理(AnalysisEventListener)。

4、xls和xlsx区别
03和07版本的写,就是对象不同,方法一样

最大行列得数量不同:

  xls最大只有65536行、256列

  xlsx可以有1048576行、16384列

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
    <!--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>

TEXT

1
2
3
4
5
6
7
8
9
10
11
12
13
poi 操作xls的
poi-ooxml 操作xlsx的

操作的版本不同,使用的工具类也不同

工作簿:
工作表:
行:
列:

5、POI Excel 写 03和07版本方式
5.1小数据量
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;

/**

  • @Classname ExcelWrite

  • @Description TODO

  • @Date 2022/1/7 12:41

  • @Created by 闲言
    */
    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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
注意对象一个区别,文件后缀

5.2大文件写HSSF(03)
缺点:最多只能处理65536行,否则会抛异常

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

优点:过程中写入缓存,不操作磁盘,最后一次性吸入磁盘,速度快

@Test
public void Write03BigData() throws Exception{

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
    //时间
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));
}

TEXT

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
结果:

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
@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));
}

TEXT

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
结果:

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

注意:

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
    //时间
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));
}

TEXT

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
  SXSSFWorkbook-来至官方的解释︰实现”BigGridDemo”策略的流式XSSFWorkbook版本。这允许写入非常大的文件而不会耗尽内存,因为任何时候只有可配置的行部分被保存在内存中。

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

6、POI Excel读 03和07版本方式
6.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);

1
2
3
4
    fis.close();
}

TEXT

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
  注意获取值的类型即可

6.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();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
  注意获取值的类型即可

6.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();

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
                            }
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();
}

TEXT

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
  如果是07版本的Excel ,只需要将HSSFWorkbook类修改为XSSFWorkbook类。将xls文件修改为xlsx文件即可

测试:读取以下表格内容

结果:

7 EasyExcel操作
7.1导入依赖

com.alibaba
easyexcel
2.2.0-beta2

1
2
3
4
5
6
7.2写入测试
1.格式类

@Getter
@Setter
@EqualsAndHashCode
public class DemoData {
@ExcelProperty(“字符串标题”)
private String string;
@ExcelProperty(“日期标题”)
private Date date;
@ExcelProperty(“数字标题”)
private Double doubleData;
/**
* 忽略这个字段
*/
@ExcelIgnore
private String ignore;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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
}

TEXT

1
2
3
4
5
6
7
8
9
10
结果

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

7.3读测试
演示读取以下excel表格数据

1.格式类

@Getter
@Setter
@EqualsAndHashCode
public class DemoData {
private String string;
private Date date;
private Double doubleData;
}
1
2
3
4
5
6
7
8
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 {

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
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("存储数据库成功");
}

TEXT

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
DAO类(不操作数据库,用不到)

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

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

1
2
3
}

TEXT

1
2
3
4
5
6
结果


Java


Pol&&Excel
https://lfrok.top/2022/11/27/Java/POI&&EasyExcel/
作者
B612🚀
发布于
2022年11月27日
许可协议