目录
- 1、常用信息
- 2、Apache POI
- 3、easyExcel
- 4、xls和xlsx区别
- 5、POI Excel 写 03和07版本方式
- 5.1小数据量
- 5.2大文件写HSSF(03)
- 5.3大文件写XSSF(07)
- 5.4大文件写SXSSF
- 6、POI Excel读 03和07版本方式
- 6.1 (03版本)
- 6.2(07版本)
- 6.3读取不同的数据类型
- 7 EasyExcel操作
- 7.1导入依赖
- 7.2写入测试
- 7.3读测试
链接: 面试题库
https://www.bloghut.cn/questionBank
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/
3、easyExcel
官网:
https://www.yuque.com/easyexcel/doc/easyexcel
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列
<!--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 操作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("文件生成完毕"); }}
注意对象一个区别,文件后缀
5.2大文件写HSSF(03)
缺点:最多只能处理65536行
,否则会抛异常
java.lang.IllegalArgumentException: Invalid row number (65536) outside allowable range (0..65535)
优点:过程中写入缓存,不操作磁盘,最后一次性吸入磁盘,速度快
@Test public void Write03BigData() throws Exception{ //时间 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)); }
结果:
5.3大文件写XSSF(07)
缺点:写数据时速度非常慢,非常耗内存
,也会发生内存溢出,如100万条。
优点:可以写较大数据量,如20万条
。
@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)); }
结果:
5.4大文件写SXSSF
优点:可以写非常大的数据量,如100万条甚至更多条写数据速度快,占用更少的内存
注意:
- 过程中会
产生临时文件
,需要清理临时文件 默认由100条记录被保存在内存中
,如果超过这数量,则最前面的数据被写入临时文件- 如果想自定义内存中数据的数量,可以使用
new SXSSFWorkbook(数量)
@Test public void Write07BigDataS() throws Exception{ //时间 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)); }
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); fis.close(); }
注意获取值的类型即可
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(); }
注意获取值的类型即可
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(); } 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(); }
如果是07版本的Excel ,只需要将HSSFWorkbook
类修改为XSSFWorkbook
类。将xls文件修改为xlsx
文件即可
测试:读取以下表格内容
结果:
7 EasyExcel操作
7.1导入依赖
<!--easyExcel--> <dependency> <groupId>com.alibaba</groupId> <artifactId>easyexcel</artifactId> <version>2.2.0-beta2</version> </dependency>
7.2写入测试
1.格式类
@Getter@Setter@EqualsAndHashCodepublic class DemoData { @ExcelProperty("字符串标题") private String string; @ExcelProperty("日期标题") private Date date; @ExcelProperty("数字标题") private Double doubleData; /** * 忽略这个字段 */ @ExcelIgnore private String ignore;}
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、读取:根据监听器设置的规则进行读取
7.3读测试
演示读取以下excel表格数据
1.格式类
@Getter@Setter@EqualsAndHashCodepublic class DemoData { private String string; private Date date; private Double doubleData;}
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<DemoData> { 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("存储数据库成功"); }}
DAO类(不操作数据库,用不到)
public class DemoDAO { public void save(List<DemoData> list) { // 如果是mybatis,尽量别直接调用多次insert,自己写一个mapper里面新增一个方法batchInsert,所有数据一次性插入 }}
测试
@Test public void simpleRead() throws Exception{ String fileName = PATH+"EasyTest.xlsx"; EasyExcel.read(fileName,DemoData.class,new DemoDataListener()).sheet().doRead(); }
结果