POI 和 easyExcel 的簡介
Java操作Excel的POI和easyExcel的學習與使用
本文的程式碼已經同步到碼雲,碼雲地址
在excel-demo模組裡
1.POI 和 easyExcel 的簡介
1.1Apache POI 簡介
Apache POI 官網:https://poi.apache.org/
主要功能:
1.2easyExcel 簡介
easyExcel 官網地址:https://github.com/alibaba/easyexcel
官方文件地址:https://www.yuque.com/easyexcel/doc/easyexcel
EasyExcel 是阿里巴巴開源的一個excel處理框架,以使用簡單、節省記憶體著稱。
EasyExcel 能大大減少佔用記憶體的主要原因是在解析 Excel 時沒有將檔案資料一次性全部載入到記憶體中,而是從磁碟上一行行讀取資料,逐個解析。
這是 EasyExcel 和 POI 在解析Excel時的對比圖。
2.Apache POI 的基本使用
需要知道使用excel中的幾個名詞
1、工作簿: Workbook
2、工作表: Sheet
3、行: Row
4、列: Cell
2.1 寫入操作
-
新建maven專案,引入依賴
<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> <!--日期格式化工具--> <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>
-
編寫測試類
package com.njit.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 java.io.FileOutputStream; import java.io.IOException; /** * @Author: njitzyd * @Date: 2020/10/3 16:50 * @Description: PoiWrite * @Version 1.0.0 */ public class PoiWrite { private static final String PATH = "D:\\IDEAWorkSpace\\JavaDemoCollection\\excel-demo/"; public static void main(String[] args) throws IOException { // // 1、建立一個工作簿 // Workbook workbook = new HSSFWorkbook(); // // 2、建立一個工作表 // Sheet sheet = workbook.createSheet("03版本的表"); // // 3、建立一個行 (1,1) // Row row1 = sheet.createRow(0); // // 4、建立一個單元格 // Cell cell11 = row1.createCell(0); // cell11.setCellValue("第一個單元格"); // // (1,2) // Cell cell12 = row1.createCell(1); // cell12.setCellValue(666); // // // 第二行 (2,1) // Row row2 = sheet.createRow(1); // Cell cell21 = row2.createCell(0); // cell21.setCellValue("第二個單元格"); // // (2,2) // Cell cell22 = row2.createCell(1); // String time = new DateTime().toString("yyyy-MM-dd HH:mm:ss"); // cell22.setCellValue(time); // // // 生成一張表(IO 流) 03 版本就是使用 xls結尾! // FileOutputStream fileOutputStream = new FileOutputStream(PATH + "這是03版本的表格.xls"); // // 輸出 // workbook.write(fileOutputStream); // // 關閉流 // fileOutputStream.close(); // // System.out.println("03版本的表格生成完畢!"); // 下面是07版本的表格 你可以發現基本不變,只是工作簿物件和生成的檔案結尾不一樣,其他不用改變 // 1、建立一個工作簿 Workbook workbook = new XSSFWorkbook(); // 2、建立一個工作表 Sheet sheet = workbook.createSheet("03版本的表"); // 3、建立一個行 (1,1) Row row1 = sheet.createRow(0); // 4、建立一個單元格 Cell cell11 = row1.createCell(0); cell11.setCellValue("第一個單元格"); // (1,2) Cell cell12 = row1.createCell(1); cell12.setCellValue(666); // 第二行 (2,1) Row row2 = sheet.createRow(1); Cell cell21 = row2.createCell(0); cell21.setCellValue("第二個單元格"); // (2,2) Cell cell22 = row2.createCell(1); String time = new DateTime().toString("yyyy-MM-dd HH:mm:ss"); cell22.setCellValue(time); // 生成一張表(IO 流) 07 版本就是使用 xlsx結尾! FileOutputStream fileOutputStream = new FileOutputStream(PATH + "這是07版本的表格.xlsx"); // 輸出 workbook.write(fileOutputStream); // 關閉流 fileOutputStream.close(); System.out.println("07版本的表格生成完畢!"); } }
2.2 大檔案寫HSSF
缺點:最多隻能處理65536行,否則會丟擲異常java.lang.IllegalArgumentException: Invalid row number (65536) outside allowable range (0..65535)
優點:過程中寫入快取,不操作磁碟,最後一次性寫入磁碟,速度快
public static void main(String[] args) throws IOException {
// 第一種方式 寫入03版本
// 時間
long begin = System.currentTimeMillis();
// 建立一個薄
Workbook workbook = new HSSFWorkbook();
// 建立表
Sheet sheet = workbook.createSheet();
// 寫入資料
for (int rowNum = 0; rowNum < 65537; rowNum++) {
Row row = sheet.createRow(rowNum);
for (int cellNum = 0; cellNum < 10 ; cellNum++) {
Cell cell = row.createCell(cellNum);
cell.setCellValue(cellNum);
}
}
System.out.println("傳入03版本成功");
FileOutputStream outputStream = new FileOutputStream(PATH + "testWrite03BigData.xls");
workbook.write(outputStream);
outputStream.close();
long end = System.currentTimeMillis();
System.out.println((double) (end-begin)/1000);
}
2.3大檔案寫XSSF
缺點:寫資料時速度非常慢,非常耗記憶體,也會發生記憶體溢位,如100萬條
優點:可以寫較大的資料量,如20萬條
public static void main(String[] args) throws IOException {
// 第二種方式 寫入07版本XSSF
// 時間
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 outputStream = new FileOutputStream(PATH + "testWrite07BigData.xlsx");
workbook.write(outputStream);
outputStream.close();
long end = System.currentTimeMillis();
System.out.println((double) (end-begin)/1000);
}
2.4 大檔案寫SXSSF
優點:可以寫非常大的資料量,如100萬條甚至更多條,寫資料速度快,佔用更少的記憶體
注意:
過程中會產生臨時檔案,需要清理臨時檔案
預設由100條記錄被儲存在記憶體中,如果超過這數量,則最前面的資料被寫入臨時檔案
如果想自定義記憶體中資料的數量,可以使用new SXSSFWorkbook ( 數量 )
public static void main(String[] args) throws IOException {
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 outputStream = new FileOutputStream(PATH + "testWrite07BigDataS.xlsx");
workbook.write(outputStream);
outputStream.close();
// 清除臨時檔案!
((SXSSFWorkbook) workbook).dispose();
long end = System.currentTimeMillis();
System.out.println((double) (end-begin)/1000);
}
SXSSFWorkbook-來至官方的解釋:實現“BigGridDemo”策略的流式XSSFWorkbook版本。這允許寫入非常大的檔案而不會耗盡記憶體,因為任何時候只有可配置的行部分被儲存在記憶體中。
請注意,仍然可能會消耗大量記憶體,這些記憶體基於您正在使用的功能,例如合併區域,註釋…仍然只儲存在記憶體中,因此如果廣泛使用,可能需要大量記憶體。
2.5 讀取基本操作
讀取的時候需要注意獲取值的型別即可
public static void main(String[] args) throws IOException {
// 03版本
// 獲取檔案流
FileInputStream inputStream = new FileInputStream(PATH + "這是03版本的表格.xls");
// 1、建立一個工作簿。 使用excel能操作的這邊他都可以操作!
Workbook workbook = new HSSFWorkbook(inputStream);
// 2、得到表
Sheet sheet = workbook.getSheetAt(0);
// 3、得到行
Row row = sheet.getRow(0);
// 4、得到列
Cell cell = row.getCell(1);
// 讀取值的時候,一定需要注意型別!
// getStringCellValue 字串型別
System.out.println(cell.getStringCellValue());
System.out.println(cell.getNumericCellValue());
inputStream.close();
// // 07 版本
// // 獲取檔案流
// FileInputStream inputStream = new FileInputStream(PATH + "這是07版本的表格.xlsx");
//
// // 1、建立一個工作簿。 使用excel能操作的這邊他都可以操作!
// Workbook workbook = new XSSFWorkbook(inputStream);
// // 2、得到表
// Sheet sheet = workbook.getSheetAt(0);
// // 3、得到行
// Row row = sheet.getRow(0);
// // 4、得到列
// Cell cell = row.getCell(1);
//
// // 讀取值的時候,一定需要注意型別!
// // getStringCellValue 字串型別
// // System.out.println(cell.getStringCellValue());
// System.out.println(cell.getNumericCellValue());
// inputStream.close();
}
2.6 讀取不同的資料型別
// 這裡只進行一種演示,03和07 的差別和其他地方一樣處理就好,只是很細微的改變,使用方法是相同的
public class PoiReadDiffType {
private static final String PATH = "D:/IDEAWorkSpace/JavaDemoCollection/excel-demo/";
public static void main(String[] args) throws IOException {
// 獲取檔案流
FileInputStream inputStream = new FileInputStream(PATH + "會員消費商品明細表.xls");
// 建立一個工作簿。 使用excel能操作的這邊他都可以操作!
Workbook workbook = new HSSFWorkbook(inputStream);
Sheet sheet = workbook.getSheetAt(0);
// 獲取標題內容
Row rowTitle = sheet.getRow(0);
if (rowTitle!=null) {
// 一定要掌握
int cellCount = rowTitle.getPhysicalNumberOfCells();
for (int cellNum = 0; cellNum < cellCount; cellNum++) {
Cell cell = rowTitle.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 HSSFCell.CELL_TYPE_STRING:
System.out.print("【String】");
cellValue = cell.getStringCellValue();
break;
// 布林
case HSSFCell.CELL_TYPE_BOOLEAN:
System.out.print("【BOOLEAN】");
cellValue = String.valueOf(cell.getBooleanCellValue());
break;
// 空
case HSSFCell.CELL_TYPE_BLANK:
System.out.print("【BLANK】");
break;
// 數字(日期、普通數字)
case HSSFCell.CELL_TYPE_NUMERIC:
System.out.print("【NUMERIC】");
// 日期
if (HSSFDateUtil.isCellDateFormatted(cell)){
System.out.print("【日期】");
Date date = cell.getDateCellValue();
cellValue = new DateTime(date).toString("yyyy-MM-dd");
}else {
// 不是日期格式,防止數字過長!
System.out.print("【轉換為字串輸出】");
cell.setCellType(HSSFCell.CELL_TYPE_STRING);
cellValue = cell.toString();
}
break;
case HSSFCell.CELL_TYPE_ERROR:
System.out.print("【資料型別錯誤】");
break;
}
System.out.println(cellValue);
}
}
}
}
inputStream.close();
}
}
2.7 excel的計算公式(瞭解)
public class Formlua {
private static final String PATH = "D:/IDEAWorkSpace/JavaDemoCollection/excel-demo/";
public static void main(String[] args) throws FileNotFoundException {
FileInputStream inputStream = new FileInputStream(PATH + "計算公式.xls");
Workbook workbook = new HSSFWorkbook(inputStream);
Sheet sheet = workbook.getSheetAt(0);
Row row = sheet.getRow(4);
Cell cell = row.getCell(0);
// 拿到計算公式 eval
FormulaEvaluator FormulaEvaluator = new HSSFFormulaEvaluator((HSSFWorkbook)workbook);
// 輸出單元格的內容
int cellType = cell.getCellType();
switch (cellType){
// 公式
case Cell.CELL_TYPE_FORMULA:
String formula = cell.getCellFormula();
System.out.println(formula);
// 計算
CellValue evaluate = FormulaEvaluator.evaluate(cell);
String cellValue = evaluate.formatAsString();
System.out.println(cellValue);
break;
}
}
}
3.EasyExcel的使用
-
匯入依賴
<dependency> <groupId>com.alibaba</groupId> <artifactId>easyexcel</artifactId> <version>2.2.3</version> </dependency>
-
新建一個實體類
easyExcel可以根據這個實體類去生成表
@Data
public class DemoData {
@ExcelProperty("字串標題")
private String string;
@ExcelProperty("日期標題")
private Date date;
@ExcelProperty("數字標題")
private Double doubleData;
/**
* 忽略這個欄位
*/
@ExcelIgnore
private String ignore;
}
- 官方的案例程式碼
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;
}
- 寫入案例
// 根據list 寫入excel
public void simpleWrite() {
// 寫法1
String fileName = PATH + "EasyTest.xlsx";
// 這裡 需要指定寫用哪個class去寫,然後寫到第一個sheet,名字為模板 然後檔案流會自動關閉
// write (fileName, 格式類)
// sheet (表名)
// doWrite (資料)
EasyExcel.write(fileName, DemoData.class).sheet("模板").doWrite(data());
}
- 讀取案例
// 先寫一個持久層,如果需要的話
/**
* 假設這個是你的DAO儲存。當然還要這個類讓spring管理,當然你不用需要儲存,也不需要這個類。
**/
public class DemoDAO {
public void save(List<DemoData> list) {
// 持久化操作!
// 如果是mybatis,儘量別直接呼叫多次insert,自己寫一個mapper裡面新增一個方法batchInsert,所有資料一次性插入
}
}
// 有個很重要的點 DemoDataListener 不能被spring管理,要每次讀取excel都要new,然後裡面用到spring可以構造方法傳進去
public class DemoDataListener extends AnalysisEventListener<DemoData> {
private static final Logger LOGGER = LoggerFactory.getLogger(DemoDataListener.class);
private static final int BATCH_COUNT = 5;
List<DemoData> list = new ArrayList<DemoData>();
private DemoDAO demoDAO;
public DemoDataListener() {
// 這裡是demo,所以隨便new一個。實際使用如果到了spring,請使用下面的有參建構函式
demoDAO = new DemoDAO();
}
public DemoDataListener(DemoDAO demoDAO) {
this.demoDAO = demoDAO;
}
// 讀取資料會執行 invoke 方法
// DemoData 型別
// AnalysisContext 分析上問
@Override
public void invoke(DemoData data, AnalysisContext context) {
System.out.println(JSON.toJSONString(data));
list.add(data);
// 達到BATCH_COUNT了,需要去儲存一次資料庫,防止資料幾萬條資料在記憶體,容易OOM
if (list.size() >= BATCH_COUNT) {
saveData(); // 持久化邏輯!
// 儲存完成清理 list
list.clear();
}
}
/**
* 所有資料解析完成了 都會來呼叫
*
* @param context
*/
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
// 這裡也要儲存資料,確保最後遺留的資料也儲存到資料庫
saveData();
LOGGER.info("所有資料解析完成!");
}
/**
* 加上儲存資料庫
*/
private void saveData() {
LOGGER.info("{}條資料,開始儲存資料庫!", list.size());
demoDAO.save(list);
LOGGER.info("儲存資料庫成功!");
}
}
// 最後的讀取方法
public void simpleRead() {
// 有個很重要的點 DemoDataListener 不能被spring管理,要每次讀取excel都要new,然後裡面用到spring可以構造方法傳進去
// 寫法1:
String fileName = PATH + "EasyTest.xlsx";
// 這裡 需要指定讀用哪個class去讀,然後讀取第一個sheet 檔案流會自動關閉
// 重點注意讀取的邏輯 DemoDataListener
EasyExcel.read(fileName, DemoData.class, new DemoDataListener()).sheet().doRead();
}
固定套路:
1、寫入,固定類格式進行寫入
2、讀取,根據監聽器設定的規則進行讀取!
更復雜的可以直接看官方的文件,很清晰!
相關文章
- Java操作Excel:POI和EasyExcelJavaExcel
- EXCEL,POI,EASYEXCEL的使用和比較Excel
- easyExcel & poi 比較Excel
- POI-簡介(1/2)
- 比 poi匯入匯出更好用的 EasyExcel使用小結Excel
- HTTP介紹和HTML簡介HTTPHTML
- JSON簡介和Ajax簡介--bea這兩篇文章介紹的不錯JSON
- git和ES6的簡介Git
- springmvc簡介和快速搭建SpringMVC
- chgrp 和 newgrp 命令簡介
- easyExcel使用Excel
- C#中的char和string的使用簡介C#
- Nginx簡介和常用的一些配置Nginx
- jvm的新生代和老年代簡介JVM
- java設計模式一一設計模式的簡介和介紹Java設計模式
- ARM MMU和cache簡介 --20240310
- 程式和執行緒簡介執行緒
- 2、Spring Cloud和dubbo簡介SpringCloud
- TCP 和 UDP 協議簡介TCPUDP協議
- 【輪子】EasyExcel,簡易版匯入、匯入 Excel、CSVExcel
- ABAP的OPEN SQL和Hybris Commerce的Flexible Search簡介SQLFlex
- 自助分析工具Power BI的簡介和應用
- Hudson的簡介
- SVN的簡介
- spark 的簡介Spark
- 關於EasyExcel的資料匯入和單sheet和多sheet匯出Excel
- EasyExcel 匯出Excel
- 初次邂逅 EasyExcelExcel
- EasyExcel匯入Excel
- ARouter簡單入門和介紹
- W3C 和 MDN 簡介
- 1.01 容器技術和docker簡介Docker
- React元件和生命週期簡介React元件
- VFS簡介和核心操作函式函式
- iptables基礎原理和使用簡介
- Go之NSQ簡介,原理和使用Go
- 樹和二叉樹簡介二叉樹
- Json-schema簡介和應用JSON