还记得IO里说过一句话吗?一切皆字节,excel的xls固然也能够用IO流读写。java
但至关的麻烦,因此须要借助第三方接口、组件快速操做Excel!git
操做Excel目前比较流行的就是Apache 的POI 和 阿里巴巴的easyExcelgithub
什么是POI ?数据库
Apache POI 是Apache软件基金会的开放源码函式库,POI提供API给Java程序对Microsoft Office格式档案读和写的功能。apache
原生的源码库使用起来会比较麻烦,因而出现了easyExcel。编程
POI官网缓存
首先建立一个空项目框架
在项目结构(快捷键Ctrl + Shift + Alt + s)中选择合适的SDKxss
在Modules模块添加POI项目工具
下一步完成项目建立!
在pom.xml中导入依赖
<!-- 导入依赖--> <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>
导入依赖时会显示不少红色错误,稍等几分钟等待IDEA自动下载依赖后就不报错了。
编写相关代码
package com.poi; 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.Test; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; public class ExcelWriteTest { @Test public void testWirte01() throws IOException { // 1.建立工做簿 HSSF是03版本 Workbook workbook = new HSSFWorkbook(); // 2.经过工做薄 -> 建立工做表 Sheet sheet = workbook.createSheet("成绩表"); // 3.经过工做表 -> 建立行 Row row1 = sheet.createRow(0); // 4.经过行 -> 建立单元格(A1) 并填充数据 Cell cell = row1.createCell(0); cell.setCellValue("张三"); Cell cell2 = row1.createCell(1); cell2.setCellValue(99); Cell cell3 = row1.createCell(2); String time = new DateTime().toString("yyyy-MM-dd HH:mm:ss"); cell3.setCellValue(time); // 保存文件 File file = new File(".\\成绩表.xls"); FileOutputStream fos = null; try{ fos = new FileOutputStream(file); workbook.write(fos); System.out.println("完成"); } catch (FileNotFoundException e) { e.printStackTrace(); } finally { if (fos != null) { fos.close(); } } } @Test public void testWirte02(){ // 1.建立工做簿 XSSF是07版本 Workbook workbook = new XSSFWorkbook(); // 2.经过工做薄 -> 建立工做表 Sheet sheet = workbook.createSheet("成绩表"); // 3.经过工做表 -> 建立行 Row row1 = sheet.createRow(0); // 4.经过行 -> 建立单元格(A1) 并填充数据 Cell cell = row1.createCell(0); cell.setCellValue("张三"); Cell cell2 = row1.createCell(1); cell2.setCellValue(99); Cell cell3 = row1.createCell(2); String time = new DateTime().toString("yyyy-MM-dd HH:mm:ss"); cell3.setCellValue(time); // 保存文件 File file = new File(".\\成绩表2.xlsx"); FileOutputStream fos = null; try{ fos = new FileOutputStream(file); workbook.write(fos); System.out.println("完成"); } catch (IOException e) { e.printStackTrace(); } finally { if (fos != null) { try { fos.close(); } catch (IOException e) { e.printStackTrace(); } } } } }
面向接口编程的好处, testWirte02只将HSSFWorkbook()修改为XSSFWorkbook() 其余的大量代码相同。
还有最后保存文件时,注意两个版本的文件后缀是不同的
@Test public void testWirte01() throws IOException { // 开始时间 long s = System.currentTimeMillis(); // 1.建立工做簿 HSSF是03版本 Workbook workbook = new HSSFWorkbook(); // 2.经过工做薄 -> 建立工做表 Sheet sheet = workbook.createSheet("测试表"); // 3.大量数据写入 03版一旦rowNum超过65536就会报错 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+1); } } // 保存文件 File file = new File(".\\03版填满.xls"); FileOutputStream fos = null; try{ fos = new FileOutputStream(file); workbook.write(fos); System.out.println("完成"); } catch (FileNotFoundException e) { e.printStackTrace(); } finally { if (fos != null) { fos.close(); } } long e = System.currentTimeMillis(); System.out.println((double)(e-s)/1000); }
@Test public void testWirte02() throws IOException { // 开始时间 long s = System.currentTimeMillis(); // 1.建立工做簿 HSSF是03版本 Workbook workbook = new XSSFWorkbook(); // 2.经过工做薄 -> 建立工做表 Sheet sheet = workbook.createSheet("测试表"); // 3.大量数据写入 XSSF 07版不限制行数 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+1); } } // 保存文件 File file = new File(".\\07版填满.xlsx"); FileOutputStream fos = null; try{ fos = new FileOutputStream(file); workbook.write(fos); System.out.println("完成"); } catch (FileNotFoundException e) { e.printStackTrace(); } finally { if (fos != null) { fos.close(); } } long e = System.currentTimeMillis(); System.out.println((double)(e-s)/1000); }
@Test public void testWirte03() throws IOException { // 开始时间 long s = System.currentTimeMillis(); // 1.建立工做簿 HSSF是03版本 Workbook workbook = new SXSSFWorkbook(); // 2.经过工做薄 -> 建立工做表 Sheet sheet = workbook.createSheet("测试表"); // 3.大量数据写入 XSSF 07版不限制行数 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+1); } } // 保存文件 File file = new File(".\\07版填满增强.xlsx"); FileOutputStream fos = null; try{ fos = new FileOutputStream(file); workbook.write(fos); System.out.println("完成"); } catch (FileNotFoundException e) { e.printStackTrace(); } finally { if (fos != null) { fos.close(); } } // 清理临时文件 ((SXSSFWorkbook) workbook).dispose(); long e = System.currentTimeMillis(); System.out.println((double)(e-s)/1000); }
SXSSF又快又能够写入大量数据
package com.poi; 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.junit.Test; import java.io.FileInputStream; public class ExcelReadTest { @Test public void testRead03() throws Exception { // 获取文件流 FileInputStream fis = new FileInputStream("成绩表.xls"); // 建立工做簿 03版本 Workbook workbook = new HSSFWorkbook(fis); // 获取工做表 Sheet sheet = workbook.getSheetAt(0); // 获取行 Row row = sheet.getRow(0); // 获取单元格 Cell cell = row.getCell(0); // 获取单元格的内容 String value = cell.getStringCellValue(); System.out.println(value); fis.close(); } @Test public void testRead07() throws Exception { // 获取文件流 FileInputStream fis = new FileInputStream("成绩表2.xlsx"); // 建立工做簿 07版本 Workbook workbook = new XSSFWorkbook(fis); // 获取工做表 Sheet sheet = workbook.getSheetAt(0); // 获取行 Row row = sheet.getRow(0); // 获取单元格 Cell cell = row.getCell(0); // 获取单元格的内容 String value = cell.getStringCellValue(); System.out.println(value); fis.close(); } }
test.xls
学号 | 姓名 | 课程 | 分数 |
---|---|---|---|
17003009 | 张三 | 高数 | 95 |
17003008 | 李四 | 计网 | 90 |
17003007 | 王五 | 大英 | 75 |
260 |
获取单元格的公式(了解)
上表格中分数汇总的260是=SUM(D2:D4)
获得的
@Test public void testFormula() throws Exception { // 首先拿到文件 FileInputStream fis = new FileInputStream(".\\test.xls"); // 获取工做牌 Workbook workbook = new HSSFWorkbook(fis); // 获取工做表 Sheet sheet = workbook.getSheetAt(0); // 定位单元格 Row row = sheet.getRow(4); Cell cell = row.getCell(3); // 直接获取单元格的值 double cellValue = cell.getNumericCellValue(); System.out.println("单元格的值:" + cellValue); // 建立公式类实例 FormulaEvaluator formulaEvaluator = new HSSFFormulaEvaluator((HSSFWorkbook) workbook); //输出单元格公式之内容 int cellType = cell.getCellType(); switch (cellType) { case CELL_TYPE_FORMULA: // 拿到计算公式 String formula = cell.getCellFormula(); System.out.println("单元格的公式:" + formula); // 统计计算器计算单元格的值 CellValue value = formulaEvaluator.evaluate(cell); // 打印的结果 String s = value.formatAsString(); System.out.println("公式算来的值:" + s); break; default: break; } fis.close(); }
运行结果
单元格的值:260.0 单元格的公式:SUM(D2:D4) 公式算来的值:260.0
封装成的工具类
package com.poi; import org.apache.poi.hssf.usermodel.HSSFCell; import org.apache.poi.hssf.usermodel.HSSFDateUtil; import org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator; import org.apache.poi.hssf.usermodel.HSSFWorkbook; import org.apache.poi.ss.usermodel.*; import org.joda.time.DateTime; import org.junit.Test; import java.io.FileInputStream; import java.util.Date; import static org.apache.poi.ss.usermodel.Cell.*; public class ExcelPOIUtil { @Test public void testCellType() throws Exception { // 获取文件流 改造工具类是把下面这行写到方法参数,经过传路径实现读表 FileInputStream fis = new FileInputStream("test.xls"); // 建立工做簿 Workbook workbook = new HSSFWorkbook(fis); // 获取工做表 Sheet sheet = workbook.getSheetAt(0); // 读取第一行 属性字段 Row rowTitle = sheet.getRow(0); if (rowTitle != null) { int cells = rowTitle.getPhysicalNumberOfCells(); for (int i = 0; i < cells; i++) { // 获取单元格 Cell cell = rowTitle.getCell(i); if (cell != null) { int type = cell.getCellType(); String s = cell.getStringCellValue(); System.out.print(s + "\t"); } } } System.out.println(); int rows = sheet.getPhysicalNumberOfRows(); for (int i = 1; i < rows; i++) { Row row = sheet.getRow(i); if (row != null) { int cells = row.getPhysicalNumberOfCells(); for (int j = 0; j < cells; j++) { System.out.print("(" + (char) ('A' + i) + "" + (j + 1) + ") "); Cell cell = row.getCell(j); // 匹配类型!!难点 if (cell != null) { // 首先获取单元格数据类型,可是以数字表示 int type = cell.getCellType(); System.out.print(type); String value = ""; // 须要switch匹配相应的类型 switch (type) { case CELL_TYPE_STRING: System.out.print("【String】"); value = cell.getStringCellValue(); break; case CELL_TYPE_BOOLEAN: System.out.print("【Boolean】"); value = String.valueOf(cell.getBooleanCellValue()); break; case CELL_TYPE_NUMERIC: if (HSSFDateUtil.isCellDateFormatted(cell)) { System.out.print("【日期】"); Date data = cell.getDateCellValue(); value = new DateTime(data).toString("yyyy-HH-mm HH:mm:ss"); } else { System.out.print("【数字串】"); cell.setCellType(CELL_TYPE_STRING); value = cell.toString(); } break; case CELL_TYPE_ERROR: System.out.print("【Error】"); break; case CELL_TYPE_BLANK: System.out.print("【Blank】"); break; default: break; } System.out.println(value); } } } } fis.close(); } }
运行结果
学号 姓名 课程 分数 (B1) 0【数字串】17003009 (B2) 1【String】张三 (B3) 1【String】高数 (B4) 0【数字串】95 (C1) 0【数字串】17003008 (C2) 1【String】李四 (C3) 1【String】计网 (C4) 0【数字串】90 (D1) 0【数字串】17003007 (D2) 1【String】王五 (D3) 1【String】大英 (D4) 0【数字串】75 (E1) 1【String】 (E2) 1【String】 (E3) 3【Blank】 (E4) 2【Formula】公式=SUM(D2:D4) 结果:0.0
双手挠头,为何获取公式单独测试时可以准确计算出公式的结果260,将该模块添加到工具类以后,结果又除了问题。
查了好多资料没找到缘由,去问问狂老师,等后续解惑以后再上来更新。
easyExcel是什么?
快速、简单避免OOM的java处理Excel工具 。
Java解析、生成Excel比较有名的框架有Apache poi、jxl。
但他们都存在一个严重的问题就是很是的耗内存,poi有一套SAX模式的API能够必定程度的解决一些内存溢出的问题,
但POI仍是有一些缺陷,好比07版Excel解压缩以及解压后存储都是在内存中完成的,内存消耗依然很大。
easyexcel重写了poi对07版Excel的解析,可以本来一个3M的excel用POI sax依然须要100M左右内存下降到几M,
而且再大的excel不会出现内存溢出,03版依赖POI的sax模式。在上层作了模型转换的封装,让使用者更加简单方便
首先从easyExcel项目上拷贝pom.xml的依赖
<groupId>com.alibaba</groupId> <artifactId>easyexcel</artifactId> <version>2.2.3</version>
了解面向对象的思想,学会面向接口编程。
理解使用测试API
视频教程Bilibili:碰见狂神说 关注公众号:狂神说
做业:后期把easyExcel官方文档的API都测试一下,再补充上面easyExcel知识。