還記得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知識。