POI和EasyExcel操作Excel

常用场景

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

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

开发中经常会设计到excel的处理,如导出Excel,导入Excel到数据库中!

操作Excel目前比较流行的就是Apache POI 和 阿里巴巴的 Easyexcel

Apache POI

Apache POI 官网地址:https://poi.apache.org/会比较麻烦,比较消耗内存出现OOM(内存溢出异常)


EasyExcel

EasyExcel官网地址:https://github.com/alibaba/easyexcel

EasyExcel 是阿里巴巴开源的一个excel处理框架,以使用简单、节省内存著称

EasyExcel 能大大减少占用内存的主要原因是在解析 Excel 时没有将文件数据一次性全部加载到内存中,而是从磁盘上一行行读取数据,逐个解析。

内存问题:POI = 100w先加载到内存 OOM。。写入文件 es = 1

下图是 EasyExcel 和 POI 在解析Excel时的对比图。

EasyExcel 使用POI的sax模式一行一行解析,并将一行的解析结果以观察者的模式通知处理。

官方文档:https://www.yuque.com/easyexcel/doc/easyexcel

POI操作Excel

POI-Excel写

创建项目

1、创建一个空项目test,创建普通Maven的Moudle demo-poi

2、引入依赖

<dependencies><!--xls(03)--><dependency><groupId>org.apache.poi</groupId><artifactId>poi</artifactId><version>3.9</version></dependency><!--xls(07)--><dependency><groupId>org.apache.poi</groupId><artifactId>poi-ooxml</artifactId><version>3.9</version></dependency><!--日期格式化工具--><dependency><groupId>joda-time</groupId><artifactId>joda-time</artifactId><version>2.10.1</version></dependency><!--test--><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version></dependency>
</dependencies>

03 | 07 版本的写,就是对象不同,方法一样的!

需要注意:2003版本和2007版本存在兼容性的问题!03最多只能有65536行!而07最多只能有1048576行!

1、工作簿:Workbook

2、工作表:Sheet

3、行:Row

4、列:Cell

03版本:

// 1、创建一个工作簿
Workbook workbook = new HSSFWorkbook();
// 2、创建一个工作表
Sheet sheet = workbook.createSheet("角色统计表");
// 3、创建一个行 (1,1)
Row row1 = sheet.createRow(0);
// 4、创建一个单元格
Cell cell1 = row1.createCell(0);
cell1.setCellValue("角色名称");
// (1,2)
Cell cell2 = row1.createCell(1);
cell2.setCellValue("角色性别");
// (1,3)
Cell cell3 = row1.createCell(2);
cell3.setCellValue("角色创建时间");// 创建第二行 (2,1)
Row row2 = sheet.createRow(1);
Cell cell21 = row2.createCell(0);
cell21.setCellValue("神里凌华");
// (2,2)
Cell cell22 = row2.createCell(1);
cell22.setCellValue("女");
// (2,3)
Cell cell23 = row2.createCell(2);
String time = new DateTime().toString("yyyy-mm-dd HH:mm:ss");
cell23.setCellValue(time);String PATH = "D:/桌面/test/";
// 生成一张表 (IO 流) 03 版本就是使用 xls结尾!
FileOutputStream fileOutputStream = new FileOutputStream(PATH + "角色统计表03.xls");
// 将IO流写入到Excel中
workbook.write(fileOutputStream);
// 刷新输出流
fileOutputStream.flush();
// 关闭输出流
fileOutputStream.close();
System.out.println("角色统计表03 生成完毕");

运行结果如下:



07版本:

// 1、创建一个工作簿
Workbook workbook = new XSSFWorkbook();
// 2、创建一个工作表
Sheet sheet = workbook.createSheet("角色统计表");
// 3、创建一个行 (1,1)
Row row1 = sheet.createRow(0);
// 4、创建一个单元格
Cell cell1 = row1.createCell(0);
cell1.setCellValue("角色名称");
// (1,2)
Cell cell2 = row1.createCell(1);
cell2.setCellValue("角色性别");
// (1,3)
Cell cell3 = row1.createCell(2);
cell3.setCellValue("角色创建时间");// 创建第二行 (2,1)
Row row2 = sheet.createRow(1);
Cell cell21 = row2.createCell(0);
cell21.setCellValue("神里凌华");
// (2,2)
Cell cell22 = row2.createCell(1);
cell22.setCellValue("女");
// (2,3)
Cell cell23 = row2.createCell(2);
String time = new DateTime().toString("yyyy-mm-dd HH:mm:ss");
cell23.setCellValue(time);String PATH = "D:/桌面/test/";
// 生成一张表 (IO 流) 07 版本就是使用 xlsx结尾!
FileOutputStream fileOutputStream = new FileOutputStream(PATH + "角色统计表07.xlsx");
// 将IO流写入到Excel中
workbook.write(fileOutputStream);
// 刷新输出流
fileOutputStream.flush();
// 关闭输出流
fileOutputStream.close();
System.out.println("角色统计表07 生成完毕");

运行结果如下:



注意对象的区别,文件后缀!

大文件写HSSF

缺点:最多只能处理65536行,否则会抛出异常

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

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

// 开始时间
long begin = System.currentTimeMillis();
// 创建一个簿
Workbook workbook = new HSSFWorkbook();
// 创建表
Sheet sheet = workbook.createSheet();
//写入数据
for (int rowNum = 0; rowNum < 65536; rowNum++) {Row row = sheet.createRow(rowNum);for (int cellNum = 0; cellNum < 10; cellNum++) {Cell cell = row.createCell(cellNum);cell.setCellValue(cellNum);}
}
System.out.println("over");
String PATH = "D:/桌面/test/";
FileOutputStream fileOutputStream = new FileOutputStream(PATH + "BigData03.xls");
workbook.write(fileOutputStream);
fileOutputStream.flush();
fileOutputStream.close();
// 结束时间
long end = System.currentTimeMillis();
System.out.println((double) (end - begin) / 1000);

运行结果如下:



大文件写XSSF

缺点:写数据时速度非常慢,非常消耗内存,也会发生内存溢出,如100万条

优点:可以写较大的数据量,如20万条

// 开始时间
long begin = System.currentTimeMillis();
// 创建一个簿
Workbook workbook = new XSSFWorkbook();
// 创建表
Sheet sheet = workbook.createSheet();
//写入数据
for (int rowNum = 0; rowNum < 100000; rowNum++) {Row row = sheet.createRow(rowNum);for (int cellNum = 0; cellNum < 10; cellNum++) {Cell cell = row.createCell(cellNum);cell.setCellValue(cellNum);}
}
System.out.println("over");
FileOutputStream fileOutputStream = new FileOutputStream(PATH + "BigData07.xlsx");
workbook.write(fileOutputStream);
fileOutputStream.flush();
fileOutputStream.close();
// 结束时间
long end = System.currentTimeMillis();
System.out.println((double) (end - begin) / 1000);

运行结果如下:



大文件写SXSSF

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

注意:

过程中会产生临时文件,需要清理临时文件

默认由100条记录会被保存在内存中,如果超过这个数量,则最前面的数据被写入临时文件

如果想自定义内存中数据的数量,可以使用new SXSSFWorkbook(数量)

// 开始时间
long begin = System.currentTimeMillis();
// 创建一个簿
Workbook workbook = new SXSSFWorkbook();
// 创建表
Sheet sheet = workbook.createSheet();
//写入数据
for (int rowNum = 0; rowNum < 100000; rowNum++) {Row row = sheet.createRow(rowNum);for (int cellNum = 0; cellNum < 10; cellNum++) {Cell cell = row.createCell(cellNum);cell.setCellValue(cellNum);}
}
System.out.println("over");
FileOutputStream fileOutputStream = new FileOutputStream(PATH + "BigData07S.xlsx");
workbook.write(fileOutputStream);
fileOutputStream.flush();
fileOutputStream.close();
// 清除临时文件
((SXSSFWorkbook) workbook).dispose();
// 结束时间
long end = System.currentTimeMillis();
System.out.println((double) (end - begin) / 1000);

运行结果如下:



SXSSFWorkbook-来至官方的解释:实现"BigGridDemo"策略的流式XSSFWorkbook版本。这允许写入非常大的文件而不会耗尽内存,因为任何时候只有可配置的行部分被保存在内存中。

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

再使用 POI的时候!内存问题Jprofile !

POI-Excel读

03 | 07

03版本

String PATH = "D:/桌面/test/";
// 获取文件流
FileInputStream fileInputStream = new FileInputStream(PATH + "角色统计表03.xls");
// 1、创建一个工作簿。使用excel能操作的这边他都可以操作!
Workbook workbook = new HSSFWorkbook(fileInputStream);
// 2、通过工作簿获取工作表,表中的设置
Sheet sheet = workbook.getSheetAt(0);
// 获得行
Row row = sheet.getRow(0);
// 获得列
Cell cell = row.getCell(3);
// 读取值的时候,一定需要注意数据类型
// getStringCellValue 字符串类型
//        System.out.println(cell.getStringCellValue());
System.out.println(cell.getNumericCellValue());
// 关闭文件流
fileInputStream.close();

运行结果如下:


07版本

String PATH = "D:/桌面/test/";
// 获取文件流
FileInputStream fileInputStream = new FileInputStream(PATH + "角色统计表07.xlsx");
// 1、创建一个工作簿。使用excel能操作的这边他都可以操作!
Workbook workbook = new XSSFWorkbook(fileInputStream);
// 2、通过工作簿获取工作表,表中的设置
Sheet sheet = workbook.getSheetAt(0);
// 获得行
Row row = sheet.getRow(0);
// 获得列
Cell cell = row.getCell(2);
// 读取值的时候,一定需要注意数据类型
// getStringCellValue 字符串类型
System.out.println(cell.getStringCellValue());
// 关闭文件流
fileInputStream.close();

运行结果如下:

注意获取列的数据类型即可

读取不同的类型数据(最麻烦的就是这里了!)

Java代码如:

String PATH = "D:/桌面/test/";
// 读取文件流
FileInputStream fileInputStream = new FileInputStream(PATH + "明细表.xlsx");
// 获取工作簿
Workbook workbook = new XSSFWorkbook(fileInputStream);
// 获取工作表
Sheet sheet = workbook.getSheetAt(0);
// 获取标题内容
Row rowTile = sheet.getRow(0);
if (rowTile != null) {int cellCount = rowTile.getPhysicalNumberOfCells();for (int cellNum = 0; cellNum < cellCount; cellNum++) {Cell cell = rowTile.getCell(cellNum);if (cell != null) {int cellType = cell.getCellType();String cellValue = cell.getStringCellValue();System.out.print(cellValue + " | ");}}System.out.println();
}// 获取表中的内容
int rowCount = sheet.getPhysicalNumberOfRows();
for (int rowNum = 1; rowNum < rowCount; rowNum++) {Row rowData = sheet.getRow(rowNum);if (rowData != null) {// 读取列int cellCount = rowData.getPhysicalNumberOfCells();for (int cellNum = 0; cellNum < cellCount; cellNum++) {System.out.print("[" + (rowNum + 1) + "-" + (cellNum + 1) + "]");Cell cell = rowData.getCell(cellNum);// 匹配列的数据类型if (cell != null) {int cellType = cell.getCellType();String cellValue = "";switch (cellType) {case XSSFCell.CELL_TYPE_STRING: // 字符串System.out.print("【String】");cellValue = cell.getStringCellValue();break;case XSSFCell.CELL_TYPE_BOOLEAN: // 布尔System.out.print("【Boolean】");cellValue = String.valueOf(cell.getBooleanCellValue());break;case XSSFCell.CELL_TYPE_BLANK: // 空的System.out.print("【Blank】");break;case XSSFCell.CELL_TYPE_NUMERIC: // 数字(日期、普通数字)System.out.print("【numeric】");if (DateUtil.isCellDateFormatted(cell)) { // 日期System.out.print("【日期】");Date date = cell.getDateCellValue();cellValue = new DateTime(date).toString("yyyy-MM-dd");} else {// 不是日期格式,防止数字过长System.out.print("【转行为字符串输出】");cell.setCellType(XSSFCell.CELL_TYPE_STRING);cellValue = cell.toString();}break;case XSSFCell.CELL_TYPE_ERROR: // 错误System.out.print("【数据类型错误】");break;}System.out.println(cellValue);}}}
}
fileInputStream.close();

运行结果如下:

注意类型转换问题:

计算公式(了解即可)

Java代码如下:

String PATH = "D:/桌面/test/";
FileInputStream fileInputStream = new FileInputStream(PATH + "公式表.xlsx");
Workbook workbook = new XSSFWorkbook(fileInputStream);
Sheet sheet = workbook.getSheetAt(0);Row row = sheet.getRow(4);
Cell cell = row.getCell(0);// 拿到计算公式 eval
FormulaEvaluator formulaEvaluator = new XSSFFormulaEvaluator((XSSFWorkbook) workbook);// 输出单元格的内容
int cellType = cell.getCellType();
switch (cellType) {case XSSFCell.CELL_TYPE_FORMULA: // 公式String formula = cell.getCellFormula();System.out.println(formula);//计算CellValue evaluate = formulaEvaluator.evaluate(cell);System.out.println(evaluate);String cellValue = evaluate.formatAsString();System.out.println(cellValue);
}

运行结果如下:

EasyExcel操作Excel

导入依赖

<dependencies><dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>2.2.0-beta2</version>
</dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.22</version></dependency><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.83</version></dependency><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version><scope>test</scope></dependency>
</dependencies>

EasyExcel写

写入测试

创建写入的实体类

@Data
public class DemoData {@ExcelProperty("字符串标题")private String string;@ExcelProperty("日期标题")private Date date;@ExcelProperty("数字标题")private Double doubleData;/*** 忽略这个字段*/@ExcelIgnoreprivate String ignore;
}

创建写入的测试类

public class DemoDataWriteTest {private List<DemoData> data() {List<DemoData> list = new ArrayList<DemoData>();for (int i = 0; i < 10; i++) {DemoData data = new DemoData();data.setString("字符串" + i);data.setDate(new Date());data.setDoubleData(0.56);list.add(data);}return list;}/*** 最简单的写* <p>1. 创建excel对应的实体对象 参照{@link DemoData}* <p>2. 直接写即可*/@Testpublic void simpleWrite() {// 写法1String fileName = TestFileUtil.getPath() + "simpleWrite" + System.currentTimeMillis() + ".xlsx";// 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭// 如果这里想使用03 则 传入excelType参数即可EasyExcel.write(fileName, DemoData.class).sheet("模板").doWrite(data());// 写法2fileName = TestFileUtil.getPath() + "simpleWrite" + System.currentTimeMillis() + ".xlsx";// 这里 需要指定写用哪个class去写ExcelWriter excelWriter = EasyExcel.write(fileName, DemoData.class).build();WriteSheet writeSheet = EasyExcel.writerSheet("模板").build();excelWriter.write(data(), writeSheet);// 千万别忘记finish 会帮忙关闭流excelWriter.finish();}private static class TestFileUtil {public static String getPath() {return "D:\\桌面\\test\\";}}
}

效果如下:

EasyExcel读

读取测试

创建读取的实体类

@Data
public class DemoData {private String string;private Date date;private Double doubleData;
}

创建读取的监听器类

// 有个很重要的点 DemoDataListener 不能被spring管理,要每次读取excel都要new,然后里面用到spring可以构造方法传进去
public class DemoDataListener extends AnalysisEventListener<DemoData> {private static final Logger LOGGER = LoggerFactory.getLogger(DemoDataListener.class);/*** 每隔5条存储数据库,实际使用中可以3000条,然后清理list ,方便内存回收*/private static final int BATCH_COUNT = 5;List<DemoData> list = new ArrayList<DemoData>();/*** 假设这个是一个DAO,当然有业务逻辑这个也可以是一个service。当然如果不用存储这个对象没用。*/private DemoDAO demoDAO;public DemoDataListener() {// 这里是demo,所以随便new一个。实际使用如果到了spring,请使用下面的有参构造函数demoDAO = new DemoDAO();}/*** 如果使用了spring,请使用这个构造方法。每次创建Listener的时候需要把spring管理的类传进来** @param demoDAO*/public DemoDataListener(DemoDAO demoDAO) {this.demoDAO = demoDAO;}/*** 这个每一条数据解析都会来调用** @param data*            one row value. Is is same as {@link AnalysisContext#readRowHolder()}* @param context*/@Overridepublic void invoke(DemoData data, AnalysisContext context) {LOGGER.info("解析到一条数据:{}", JSON.toJSONString(data));list.add(data);// 达到BATCH_COUNT了,需要去存储一次数据库,防止数据几万条数据在内存,容易OOMif (list.size() >= BATCH_COUNT) {saveData();// 存储完成清理 listlist.clear();}}/*** 所有数据解析完成了 都会来调用** @param context*/@Overridepublic void doAfterAllAnalysed(AnalysisContext context) {// 这里也要保存数据,确保最后遗留的数据也存储到数据库saveData();LOGGER.info("所有数据解析完成!");}/*** 加上存储数据库*/private void saveData() {LOGGER.info("{}条数据,开始存储数据库!", list.size());demoDAO.save(list);LOGGER.info("存储数据库成功!");}
}

创建读取的持久层类

/*** 假设这个是你的DAO存储。当然还要这个类让spring管理,当然你不用需要存储,也不需要这个类。**/
public class DemoDAO {public void save(List<DemoData> list) {// 如果是mybatis,尽量别直接调用多次insert,自己写一个mapper里面新增一个方法batchInsert,所有数据一次性插入}
}

创建读取的测试类

public class DemoDataReadTest {/*** 最简单的读* <p>1. 创建excel对应的实体对象 参照{@link DemoData}* <p>2. 由于默认一行行的读取excel,所以需要创建excel一行一行的回调监听器,参照{@link DemoDataListener}* <p>3. 直接读即可*/@Testpublic void simpleRead() {// 有个很重要的点 DemoDataListener 不能被spring管理,要每次读取excel都要new,然后里面用到spring可以构造方法传进去// 写法1:String fileName = TestFileUtil.getPath() + "test" + File.separator + "demo.xlsx";// 这里 需要指定读用哪个class去读,然后读取第一个sheet 文件流会自动关闭EasyExcel.read(fileName, DemoData.class, new DemoDataListener()).sheet().doRead();// 写法2:fileName = TestFileUtil.getPath() + "test" + File.separator + "demo.xlsx";ExcelReader excelReader = EasyExcel.read(fileName, DemoData.class, new DemoDataListener()).build();ReadSheet readSheet = EasyExcel.readSheet(0).build();excelReader.read(readSheet);// 这里千万别忘记关闭,读的时候会创建临时文件,到时磁盘会崩的excelReader.finish();}private static class TestFileUtil {public static String getPath() {return "D:\\桌面\\";}}
}

效果如下:

POI和EasyExcel操作Excel相关推荐

  1. java insert row,POI ,Java 操作 Excel 实现行的插入(insert row)

    POI ,Java 操作 Excel 实现行的插入(insert row) 前几天,正在做一个项目,主要用 POI 来操作 Excel 其中,要使用一个,插入功能.主要是因为从数据库,返回结果集(数据 ...

  2. Java使用poi/easyexcel操作excel

    前言 在工作的开发过过程中,我们总会遇到将数据导出到excel的需求,和导入excel到数据库,下面会讲解当今比较流行的两个工具实现Java操作excel: Apache POI 阿里巴巴的easye ...

  3. POI和 EasyExcel对Excel的简单操作

    目录 POI 结构 应用 Excel excel2003版和excel2007版的区别 关于WorkBook POI的写入操作 1.HSSFWorkbook的写入操作 2.XSSFWorkbook的写 ...

  4. 使用POI和EasyExcel实现Excel导入和导出功能

    需求场景 开发中经常会设计到excel的处理,需求场景如下所示: 1.将用户信息导出为excel表格(导出数据) 2.将Excel表中的信息录入到数据库中(导入数据) 操作Excel目前比较流行的就是 ...

  5. POI和EasyExcel实现Excel数据批量读取到数据库

    POI和easyExcel讲解 目的:这一部分我主要是为了实现excel数据批量插入到数据库. 参考:狂神说java的上课笔记 视屏教学网址 常用信息 1,将用户信息导出为excel表格 2,讲Exc ...

  6. java利用EasyExcel操作Excel

    EasyExcel EasyExcel读写Excel的基本使用 Excel导入导出的应用场景 数据导入:减轻录入工作量 数据导出:统计信息归档 数据传输:异构系统之间数据传输 EasyExcel简介 ...

  7. Java使用easyExcel操作Excel就是这么简单!

    文章目录 前言 第一步:导入依赖 第二步:创建一个EasyExcelReadListener类,继承easyExcel中的AnalysisEventListener类,实现里面的invoke()和do ...

  8. 使用easyexcel操作Excel

    我也是凑巧用上了,就在这里分享一下,性能确实是没得说! 对easyexcel的介绍是:Java解析.生成Excel比较有名的框架有Apache poi.jxl.但他们都存在一个严重的问题就是非常的耗内 ...

  9. POI及EasyExcel操作xls,xlsx文件

    Apache POI 是基于 Office Open XML 标准(OOXML)和 Microsoft 的 OLE 2 复合文档格式(OLE2)处理各种文件格式的开源项目. 可以使用 Java 读写 ...

最新文章

  1. 《Linux内核设计与实现》读书笔记 第三章 进程管理
  2. java 程序编译和运行的过程
  3. Codeforces 1344F Piet's Palette (线性代数、高斯消元)
  4. pmml_再访PMML
  5. er图用什么软件_工艺流程图用什么软件做?规范实用的流程图工具
  6. 拳王虚拟项目公社:2020考研专业院校选择
  7. idea 非法字符 \ufeff
  8. (转)IDE 而言,是 Xcode 的技术比较先进还是 Visual Studio?
  9. Linux守护进程的编程实现
  10. Oracle 创建用户为什么要加C##
  11. 最新阿里巴巴Java开发手册发布和下载
  12. EXCEL表格-COUNTIF函数查找数据重复项
  13. Java之HashMap系列--保证线程安全的方法
  14. 键盘选中一行或多行的快捷键
  15. VS2015基础开发流程
  16. 移动端和PC端的pdf预览与下载
  17. Mac Big Sur keygen 您没有打开权限 问题;Unpacked 0 files.问题
  18. uniapp 自定义相机人脸和人脸识别
  19. java date 操作_java日期操作-java Date-java Calendar-嗨客网
  20. 光纤之父高锟获英女王封爵

热门文章

  1. 萨提亚·纳德拉与沈向洋CVPR对谈:那些未来可期的计算机视觉研究与应用
  2. SOLIDWORKS零件与装配体模板制作
  3. Cocos2dx游戏开发素材/学习网站分享
  4. 【docker】Mac下oracle10g下载安装
  5. Ubuntu 10.10 安装谷歌拼音输入法~
  6. 四、在Xilinx FPGA上使用Cortex M1 软核——固化Cortex-M1程序
  7. 【Flutter 问题系列第 15 篇】如何给 Flutter 中的图片设置透明度
  8. 干货!ERP系统优化生产管理流程五大步骤
  9. 一个纯技术程序员的求职心酸历程
  10. 联想电脑bios设置u盘启动 windows安装 重装系统盘