Java操作Excel文件解析與讀寫方法詳解
一、概述
在應(yīng)用程序的開發(fā)過程中,經(jīng)常需要使用 Excel 文件來進(jìn)行數(shù)據(jù)的導(dǎo)入或?qū)С?。所以,在通過Java語言實(shí)現(xiàn)此 類需求的時(shí)候,往往會(huì)面臨著Excel文件的解析(導(dǎo)入)或生成(導(dǎo)出)。
在Java技術(shù)生態(tài)圈中,可以進(jìn)行Excel文件處理的主流技術(shù)包括: Apache POI 、 JXL 、 Alibaba EasyExcel 等。
二、Apache POI
Apache POI 是用 Java 編寫的免費(fèi)開源的跨平臺(tái)的 Java API , Apache POI 提供 給 Java 程序?qū)?Microsoft Office 格式檔案進(jìn)行讀寫功能的 API 開源類庫。
它分別提供對(duì)不同格式文件的解析:
- HSSF - 提供讀寫Microsoft Excel格式檔案的功能。
- XSSF - 提供讀寫Microsoft Excel OOXML格式檔案的功能。
- HWPF - 提供讀寫Microsoft Word格式檔案的功能。
- HSLF - 提供讀寫Microsoft PowerPoint格式檔案的功能。
- HDGF - 提供讀寫Microsoft Visio格式檔案的功能。
三、XSSF解析Excel文件
HSSF 用于解析舊版本(*.xls)Excel文件,由于舊版本的Excel文件只能存在65535行數(shù)據(jù),所以目前已經(jīng)不常用。所以 目前主要采用 XSSF 進(jìn)行新版本(*.xlsx)Exce文件的解析。
1.Workbook(Excel文件)
Workbook 接口代表一個(gè) Excel 文件,用于創(chuàng)建或加載(解析) Excel 文件。常見實(shí)現(xiàn)類是 XSSFWorkbook 。
創(chuàng)建Excel文件
try (Workbook workbook = new XSSFWorkbook();
FileOutputStream fos = new FileOutputStream("c:\\test\\temp.xlsx")) {
workbook.write(fos);
} catch (IOException e) {
e.printStackTrace();
}解析Excel文件
// 輸入流
FileInputStream fis = new FileInputStream("c:\\test\\1627356554991.xlsx");
// Excel文件對(duì)象
Workbook workbook = new XSSFWorkbook(fis);2.Sheet(工作簿)
通過 Workbook 來進(jìn)行工作簿 Sheet 對(duì)象的獲取或創(chuàng)建
創(chuàng)建工作簿
// 按照默認(rèn)名稱創(chuàng)建工作簿
Sheet sheet1 = workbook.createSheet();
// 按照自定義名稱創(chuàng)建工作簿
Sheet sheet2 = workbook.createSheet("自定義工作簿2");獲取工作簿
// 按照工作簿下標(biāo)獲取Sheet
Sheet sheet01 = workbook.getSheetAt(0);
// 按照工作簿名稱獲取Sheet
Sheet sheet02 = workbook.getSheet("Sheet0");獲取工作簿的數(shù)量
int n = workbook.getNumberOfSheets();
3.Row(數(shù)據(jù)行)
通過 Sheet 來進(jìn)行數(shù)據(jù)行 Row 對(duì)象的獲取或創(chuàng)建
創(chuàng)建數(shù)據(jù)行
Row row = sheet.createRow(0);
獲取首行下標(biāo)和尾行下標(biāo)
int first = sheet.getFirstRowNum(); int last = sheet.getLastRowNum();
根據(jù)下標(biāo)獲取指定行
Row row = sheet.getRow(0);
遍歷所有行
for(Row row : sheet) {
System.out.println(row);
}遍歷指定區(qū)域行
for (int i = 1; i <= sheet.getLastRowNum(); i++) {
Row row = sheet.getRow(i);
System.out.println(row);
}4.Cell(單元格)
通過 Row 來進(jìn)行單元格 Cell 對(duì)象的獲取或創(chuàng)建。
創(chuàng)建單元格
Cell cell0 = row.createCell(0);
設(shè)置單元格值
cell0.setCellValue(UUID.randomUUID().toString());
根據(jù)下標(biāo)獲取單元格
Cell cell = row.getCell(1);
遍歷所有單元格
for(Cell cell : row) {}獲取單元格的類型
CellType type = cell.getCellType();
設(shè)置單元格樣式
// 創(chuàng)建單元格樣式
DataFormat dataFormat = workbook.createDataFormat();
Short formatCode = dataFormat.getFormat("yyyy-MM-dd HH:mm:ss");
CellStyle cellStyle = workbook.createCellStyle();
cellStyle.setDataFormat(formatCode);
// 為當(dāng)前行創(chuàng)建單元格
Cell cell1 = row.createCell(1);
cell1.setCellStyle(cellStyle); // 設(shè)置單元格樣式
cell1.setCellValue(new Date()); // 保存當(dāng)前日期時(shí)間至本單元格設(shè)置單元格對(duì)齊
// 創(chuàng)建單元格樣式 CellStyle cellStyle = workbook.createCellStyle(); //設(shè)置單元格的水平對(duì)齊類型。 此時(shí)水平居中 cellStyle.setAlignment(HorizontalAlignment.CENTER); // 設(shè)置單元格的垂直對(duì)齊類型。 此時(shí)垂直靠底邊 cellStyle.setVerticalAlignment(VerticalAlignment.BOTTOM);
四、超大Excel文件讀寫
1.使用POI寫入
使用 SXSSFWorkbook 進(jìn)行寫入,通過設(shè)置 SXXFWorkbook 的構(gòu)造參數(shù),可以設(shè)置每次在內(nèi)存中保持的行 數(shù),當(dāng)達(dá)到這個(gè)值的時(shí)候,那么會(huì)把這些數(shù)據(jù) flush 到磁盤上,這樣就不會(huì)出現(xiàn)內(nèi)存不夠的情況。
try (Workbook workbook = new SXSSFWorkbook(100);
FileOutputStream fos = new FileOutputStream("c:\\test\\temp.xlsx")) {
Sheet sheet1 = workbook.createSheet();
for (int i = 0; i <= 1000000; i++) {
Row row = sheet1.createRow(i);
Cell cell0 = row.createCell(0);
cell0.setCellValue(UUID.randomUUID().toString());
Cell cell1 = row.createCell(1);
cell1.setCellValue(new Date());
}
workbook.write(fos);
} catch (IOException e) {
e.printStackTrace();
}但是讀取超大Excel時(shí)POI會(huì)把文件的所有內(nèi)容都加載到內(nèi)存中,很容易占用大量內(nèi)存;甚至發(fā)生out of memory異常。
2.使用EasyExcel
- Java領(lǐng)域解析、生成Excel比較有名的框架有Apache poi、jxl等。但他們都存在一個(gè)嚴(yán)重的問題就是非常的耗內(nèi)存。如果你的系統(tǒng)并發(fā)量不大的話可能還行,但是一旦并發(fā)上來后一定會(huì)OOM或者JVM頻繁的full gc。
- EasyExcel是阿里巴巴開源的一個(gè)excel處理框架,以使用簡單、節(jié)省內(nèi)存著稱。EasyExcel能大大減少占用內(nèi)存的主要原因是在解析Excel時(shí)沒有將文件數(shù)據(jù)一次性全部加載到內(nèi)存中,而是從磁盤上一行行讀取數(shù)據(jù),逐個(gè)解析。
- EasyExcel采用一行一行的解析模式,并將一行的解析結(jié)果以觀察者的模式通知處理。
例:
//準(zhǔn)備實(shí)體類
public class Order {
@ExcelProperty("訂單編號(hào)")
private String orderId; // 訂單編號(hào)
@ExcelProperty("支付金額")
@NumberFormat("¥#,###")
private Double payment; // 支付金額
@ExcelProperty(value = "創(chuàng)建日期",converter = LocalDateTimeConverter.class)
private LocalDateTime creationTime; // 創(chuàng)建時(shí)間
public Order() {
this.orderId = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddhhmmss"))
+ UUID.randomUUID().toString().substring(0, 5);
this.payment = Math.random() * 10000;
this.creationTime = LocalDateTime.now();
}
public String getOrderId() {
return orderId;
}
public void setOrderId(String orderId) {
this.orderId = orderId;
}
public Double getPayment() {
return payment;
}
public void setPayment(Double payment) {
this.payment = payment;
}
public LocalDateTime getCreationTime() {
return creationTime;
}
public void setCreationTime(LocalDateTime creationTime) {
this.creationTime = creationTime;
}
@Override
public String toString() {
return "Order [orderId=" + orderId + ", payment=" + payment + ", creationTime=" + creationTime + "]";
}
}//準(zhǔn)備Converter轉(zhuǎn)換類
public class LocalDateTimeConverter implements Converter<LocalDateTime> {
@Override
public Class<LocalDateTime> supportJavaTypeKey() {
return LocalDateTime.class;
}
@Override
public CellDataTypeEnum supportExcelTypeKey() {
return CellDataTypeEnum.STRING;
}
@Override
public LocalDateTime convertToJavaData(CellData cellData, ExcelContentProperty contentProperty,
GlobalConfiguration globalConfiguration) {
return LocalDateTime.parse(cellData.getStringValue(), DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
}
@Override
public CellData<String> convertToExcelData(LocalDateTime value, ExcelContentProperty contentProperty,
GlobalConfiguration globalConfiguration) {
return new CellData<>(value.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
}
}寫入數(shù)據(jù)
import java.util.ArrayList;
import java.util.List;
import com.alibaba.excel.EasyExcel;
public class Demo01 {
public static void main(String[] args) {
long begin = System.currentTimeMillis();
// 寫入100w
EasyExcel.write("D:\\java.workspace\\1000W.xlsx", Order.class)
.sheet("訂單列表")
.doWrite(data());
long end = System.currentTimeMillis();
System.out.println("共耗時(shí)"+(end-begin)+"毫秒");
}
// 創(chuàng)建100w條訂單數(shù)據(jù)
private static List<Order> data() {
List<Order> list = new ArrayList<Order>();
for (int i = 0; i < 1000000; i++) {
list.add(new Order());
}
return list;
}
}
讀取數(shù)據(jù)
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
public class Demo02 {
public static void main(String[] args) {
//用于保存讀取到的結(jié)果
List<Order> orderList = new ArrayList<Order>();
//讀取
EasyExcel.read("D:\\java.workspace\\1000W.xlsx", Order.class,new AnalysisEventListener<Order>() {
@Override
public void invoke(Order order, AnalysisContext arg1) {
// 讀取每條數(shù)據(jù)
orderList.add(order);
}
@Override
public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
// 讀取到列頭
System.out.println(headMap);
}
@Override
public void doAfterAllAnalysed(AnalysisContext arg0) {
// 讀取完畢
System.out.println("END");
}
}).sheet().doRead();
//遍歷
for(Order order : orderList) {
System.out.println(order);
}
}
}到此這篇關(guān)于Java操作Excel文件解析與讀寫方法詳解的文章就介紹到這了,更多相關(guān)Java Excel文件解析內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
List集合按某個(gè)屬性或者字段進(jìn)行分組的操作
這篇文章主要介紹了List集合按某個(gè)屬性或者字段進(jìn)行分組的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-06-06
SpringBoot3整合Swagger3時(shí)出現(xiàn)Type javax.servlet.http.H的ttpSe
這篇文章主要介紹了SpringBoot3整合Swagger3時(shí)出現(xiàn)Type javax.servlet.http.H的ttpServletRequest not present錯(cuò)誤解決方法,文中有詳細(xì)的解決方法,需要的朋友可以參考下2025-01-01
mybatis的動(dòng)態(tài)SQL和模糊查詢實(shí)例詳解
這篇文章主要給大家介紹了關(guān)于mybatis的動(dòng)態(tài)SQL和模糊查詢的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-03-03
Java中ArrayList在foreach里remove的問題詳析
這篇文章主要給大家介紹了關(guān)于Java中ArrayList在foreach里remove問題的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起看看吧2018-09-09
java短網(wǎng)址服務(wù)(TinyURL)生成算法
這篇文章主要為大家詳細(xì)介紹了java短網(wǎng)址服務(wù)生成算法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-08-08
java實(shí)現(xiàn)日歷應(yīng)用程序設(shè)計(jì)
這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)日歷應(yīng)用程序設(shè)計(jì),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-06-06

