既然 io 流都学了,那不顺便看看 java 怎么操作excel吗?
不过这些不用担心我们自己来,毕竟我们能想到的,早就有大佬造好轮子。
需要注意的是 excel 分为03版本和07版本。两个版本有着很大的差别。
03版本的 excel 以 xls 后缀结尾,最多只能存放 65536行数据
07版本的 excel 以 xlsx 后缀结尾,理论上不限制行数。
POI方式操作 excel
Apache POI是一个开源的Java读写Excel、WORD等微软OLE2组件文档的项目。
我们可以通过 poi 来达到我们的目的
环境搭建
毫无疑问,在pom 文件中引入 poi 依赖即可。
<dependencies><!--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><!--test--><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version></dependency></dependencies>
代码实现前的简单分析
要知道在 java中,一切都为对象。毫无疑问,excel 也可以被看成一个对象。那我们来分析一下这个 excel
excel 里 有 sheet,也就是工作表,
工作表里有一行一行的数据,也就是 row
一行中有很多个列 在 poi 里叫做cell,这个列就是单元格,我们的数据,就是放在这个 cell 中。
简单的写入
其实这两个版本在代码方面,并无太大的差别。因为 POI 提供了我们一个接口 Workbook。
03 版本和 07 版本都实现了这个接口。所以我们只需要改一下实现类即可。
当然,生成的文件后缀名也是要改的。
03版本的写入 HSSFWorkbook
//测试 03 版本的 excel.xls@Testpublic void testXls03() throws IOException {//1.创建一个工作簿,也就是excelWorkbook workbook = new HSSFWorkbook();//2.创建一个sheet,也就是工作表Sheet sheet = workbook.createSheet("今日份的学习");//3.创建行,下标从0开始,0就是excel 的第一行Row row1 = sheet.createRow(0);//4.创建列(单元格)//现在 这个列 就是 (1,1) 单元格Cell Cell11 = row1.createCell(0);//5.填充数据Cell11.setCellValue("POI的学习进度");//创建第一行第二列,也就是(1,2) 单元格Cell Cell12 = row1.createCell(1);Cell12.setCellValue("刚开始");//创建第二行Row row2 = sheet.createRow(1);Cell cell21 = row2.createCell(0);Cell cell22 = row2.createCell(1);cell21.setCellValue("esayExcel的学习进度");cell22.setCellValue("还没开始");//就在项目路径下创建一个 xxx.xlsFileOutputStream outputStream = new FileOutputStream(LocalDate.now().toString()+"的03excel.xls");workbook.write(outputStream);outputStream.close();}
07版本的写入 XSSFWorkbook
@Testpublic void test07() throws IOException {//1.创建一个工作簿,也就是excelWorkbook workbook = new XSSFWorkbook();//2.创建一个sheet,也就是工作表Sheet sheet = workbook.createSheet("今日份的学习");//3.创建行,下标从0开始,0就是excel 的第一行Row row1 = sheet.createRow(0);//4.创建列(单元格)//现在 这个列 就是 (1,1) 单元格Cell Cell11 = row1.createCell(0);//5.填充数据Cell11.setCellValue("POI的学习进度");//创建第一行第二列,也就是(1,2) 单元格Cell Cell12 = row1.createCell(1);Cell12.setCellValue("刚开始");//创建第二行Row row2 = sheet.createRow(1);Cell cell21 = row2.createCell(0);Cell cell22 = row2.createCell(1);cell21.setCellValue("esayExcel的学习进度");cell22.setCellValue("还没开始");//就在项目路径下创建一个 xxx.xlsxFileOutputStream outputStream = new FileOutputStream(LocalDate.now().toString()+"的07excel.xlsx");workbook.write(outputStream);outputStream.close();}
区别 HSSFWorkbook 和 XSSFWorkbook
HSSF 方式,也就是03版本,只能存放 65536行数据,如果超过这个限制,会抛异常。
但他的优点是速度快,因为它会将数据直接读取到 内存中,然后在最后一次性将所有数据写入硬盘。
不过在操作大数据的时候,可能会导致内存溢出。
而 XSSF 方式,也就是 07版本。写数据的时候非常缓慢,非常耗内存,但是可以写入更多的数据。
如果数据过多,也会导致内存溢出。为了解决这个问题,推荐使用 SXSSFWorkbook
SXSSFWorkbook
SXSSFWorkbook,是 POI 为了解决 XSSF 写入数据过慢而提供的一个Workbook实现类。
它的优点是速度快,也可以写入多量的数据,占用的内存也很少。
SXSSFWorkbook 采用临时文件的方式。
默认会将100条数据存放到内存中,如果超过100条,写入这个临时文件,然后继续读取。
这个临时文件需要我们清理。
但是需要注意的是,如果在 excel 中广泛使用合并单元格等操作的话,同样会占用大量的内存。
大数据的写入
通过上边讲述的HSSFWorkbook,XSSFWorkbook 和 SXSSFWorkbook的区别,我们来进行一下大数据的测试
HSSFWorkbook
因为我的这个代码存放的是 机械硬盘,所以读写速度过慢。
不过也可以看到,HSSF 速度还是很快的只花费了2.229 秒
测试一下 100000 条数据,会发现抛了异常。
//测试一下 03版本的大数据@Testpublic void test03BigData() throws IOException {long start = System.currentTimeMillis();//1.创建一个工作簿,也就是excelWorkbook workbook = new HSSFWorkbook();//2.创建一个sheet,也就是工作表Sheet sheet = workbook.createSheet("大数据测试");// 03 版本最多只能有 65536行数据,否则会报异常for (int i = 0; i < 65536; i++) {Row row = sheet.createRow(i);for (int j = 0; j < 10; j++) {row.createCell(j).setCellValue(i);}}System.out.println("over");//就在项目路径下创建一个 xxx.xlsFileOutputStream outputStream = new FileOutputStream(LocalDate.now().toString()+"的03bigdata.xls");workbook.write(outputStream);outputStream.close();long end = System.currentTimeMillis();System.out.println((double) (end - start) / 1000);//2.229}
XSSFWorkbook
在 XSSF 的方式下,10万条数据成功写入。
但是速度非常的慢,花费了11.206秒。
@Testpublic void test07BigData() throws IOException {long start = System.currentTimeMillis();Workbook workbook = new XSSFWorkbook();Sheet sheet = workbook.createSheet("大数据测试");// 07 版本 理论上是无限大的,可以存取很多行for (int i = 0; i < 100000; i++) {Row row = sheet.createRow(i);for (int j = 0; j < 10; j++) {row.createCell(j).setCellValue(i);}}System.out.println("over");//就在项目路径下创建一个 xxx.xlsFileOutputStream outputStream = new FileOutputStream(LocalDate.now().toString()+"的07bigdata.xlsx");workbook.write(outputStream);outputStream.close();long end = System.currentTimeMillis();System.out.println((double) (end - start) / 1000);//11.206//可以发现,速度很慢,那有没有什么办法改善一下呢。可以使用 SXSSFWorkbook}
SXSSFWorkbook
使用 SXSSFWorkBook 来给 10万条数据加速。
会发现,速度提升了很多,只花费了2.806秒
@Testpublic void test07SXSSFWorkbookBigData() throws IOException {long start = System.currentTimeMillis();Workbook workbook = new SXSSFWorkbook();Sheet sheet = workbook.createSheet("大数据测试");// 07 版本 理论上是无限大的,可以存取很多行for (int i = 0; i < 100000; i++) {Row row = sheet.createRow(i);for (int j = 0; j < 10; j++) {row.createCell(j).setCellValue(i);}}System.out.println("over");//就在项目路径下创建一个 xxx.xlsFileOutputStream outputStream = new FileOutputStream(LocalDate.now().toString()+"SXSSFWorkbook.xlsx");workbook.write(outputStream);outputStream.close();long end = System.currentTimeMillis();//清楚临时文件((SXSSFWorkbook)workbook).dispose();//这次就会发现,速度快了很多。System.out.println((double) (end - start) / 1000);//2.806}
读取excel
使用 iputStream 来读取刚才我们写入的 数据。
要注意的是读取数据的类型。这也是读取数据时最麻烦的一点。
@Testpublic void testRead() throws IOException {FileInputStream inputStream = new FileInputStream("2020-04-25的07excel.xlsx");// Workbook workbook = new HSSFWorkbook(inputStream);Workbook workbook = new XSSFWorkbook(inputStream);//获取 excel 的 第一张表Sheet sheet = workbook.getSheetAt(0);//获取第一行Row row = sheet.getRow(0);//获取第一个单元格Cell cell = row.getCell(0);//这里知道第一个单元格的数据为 String 类型,所以获取String 类型System.out.println(cell.getStringCellValue());inputStream.close();}
读取任意类型的数据
其实也很简单,因为数据类型,并不受我们控制(我们不知到excel 当前数据到底是 String 还是 int 还是 boolean)。
所以我们也没办法,只能 通过 switch 来判断。
以下以 XSSFWorkbook 示例:
HSSF 同理
@Testpublic void testReadByType() throws IOException {FileInputStream inputStream = new FileInputStream("2020-04-25的07excel.xlsx");Workbook workbook = new XSSFWorkbook(inputStream);//获取 excel 的 第一张表Sheet sheet = workbook.getSheetAt(0);//获取该 表 有多少行int rowsCount = sheet.getPhysicalNumberOfRows();for (int rowNum = 0; rowNum < rowsCount; rowNum++) {Row row = sheet.getRow(rowNum);//获取 该行有多少列中有数据int cellsCount = row.getPhysicalNumberOfCells();for (int cellNum = 0; cellNum < cellsCount; cellNum++) {Cell cell = row.getCell(cellNum);if (cell == null) {//说明,该单元格为 空单元格,没有数据cell = row.createCell(cellNum);}//判断数据的类型。switch (cell.getCellType()) {case Cell.CELL_TYPE_STRING ://字符串类型System.out.println(cell.getStringCellValue());break;case Cell.CELL_TYPE_BOOLEAN ://boolean类型System.out.println(cell.getBooleanCellValue());break;case Cell.CELL_TYPE_BLANK :System.out.println("这是一个空的单元格");break;case Cell.CELL_TYPE_NUMERIC ://当type是numberic的时候,又分为数字格式和日期格式。boolean isDate = DateUtil.isCellDateFormatted(cell);if (isDate) {//时间类型Date date = cell.getDateCellValue();LocalDate localDate = date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();System.out.println(localDate);}else {//数字类型System.out.println(cell.getNumericCellValue());}break;}}}inputStream.close();}
关于 excel 中的公式
有的时候 excel 单元格里存放的是 公式
比如:
这个时候我们怎么获取并且得到结果呢?
我们在 POI 中也可以获取 计算公式 FormulaEvaluator
然后通过 evaluate方法 获取计算结果
@Test
public void testFormula() throws IOException {
FileInputStream inputStream = new FileInputStream("2020-04-25的07excel.xlsx");
Workbook workbook = new XSSFWorkbook(inputStream);
//获取 excel 的 第一张表
Sheet sheet = workbook.getSheetAt(0);
Row row = sheet.getRow(4);
Cell cell = row.getCell(1);
//拿到计算公式
FormulaEvaluator formulaEvaluator = new XSSFFormulaEvaluator((XSSFWorkbook) workbook);
switch (cell.getCellType()) {
//可以这样写特定的类型XSSFCell
//case XSSFCell.CELL_TYPE_FORMULA
//不过最好建议这样写
case Cell.CELL_TYPE_FORMULA :
System.out.println("这是一个公式类型");
//执行公式
CellValue cellValue = formulaEvaluator.evaluate(cell);
//输出执行结果
System.out.println(cellValue.formatAsString());
break;
}
inputStream.close();
}
easyExcel
easyExcel 是阿里巴巴开发的用来解析 excel的工具,比 POI 更快,更简单,而且一定不发产生你内存溢出的状况
easyExcel github地址
easyExcel 语雀文档
非常建议了解一下。
文档很详细。
建议直接看文档
引入相关依赖
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>2.2.3</version>
</dependency>
