Java操作Excel文件解析與讀寫方法詳解
一、概述
在應用程序的開發過程中,經常需要使用 Excel 文件來進行數據的導入或導出。所以,在通過Java語言實現此 類需求的時候,往往會面臨著Excel文件的解析(導入)或生成(導出)。
在Java技術生態圈中,可以進行Excel文件處理的主流技術包括: Apache POI 、 JXL 、 Alibaba EasyExcel 等。
二、Apache POI
Apache POI 是用 Java 編寫的免費開源的跨平臺的 Java API , Apache POI 提供 給 Java 程序對 Microsoft Office 格式檔案進行讀寫功能的 API 開源類庫。
它分別提供對不同格式文件的解析:
- HSSF - 提供讀寫Microsoft Excel格式檔案的功能。
- XSSF - 提供讀寫Microsoft Excel OOXML格式檔案的功能。
- HWPF - 提供讀寫Microsoft Word格式檔案的功能。
- HSLF - 提供讀寫Microsoft PowerPoint格式檔案的功能。
- HDGF - 提供讀寫Microsoft Visio格式檔案的功能。
三、XSSF解析Excel文件
HSSF 用於解析舊版本(*.xls)Excel文件,由於舊版本的Excel文件隻能存在65535行數據,所以目前已經不常用。所以 目前主要采用 XSSF 進行新版本(*.xlsx)Exce文件的解析。
1.Workbook(Excel文件)
Workbook 接口代表一個 Excel 文件,用於創建或加載(解析) Excel 文件。常見實現類是 XSSFWorkbook 。
創建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文件對象 Workbook workbook = new XSSFWorkbook(fis);
2.Sheet(工作簿)
通過 Workbook 來進行工作簿 Sheet 對象的獲取或創建
創建工作簿
// 按照默認名稱創建工作簿 Sheet sheet1 = workbook.createSheet(); // 按照自定義名稱創建工作簿 Sheet sheet2 = workbook.createSheet("自定義工作簿2");
獲取工作簿
// 按照工作簿下標獲取Sheet Sheet sheet01 = workbook.getSheetAt(0); // 按照工作簿名稱獲取Sheet Sheet sheet02 = workbook.getSheet("Sheet0");
獲取工作簿的數量
int n = workbook.getNumberOfSheets();
3.Row(數據行)
通過 Sheet 來進行數據行 Row 對象的獲取或創建
創建數據行
Row row = sheet.createRow(0);
獲取首行下標和尾行下標
int first = sheet.getFirstRowNum(); int last = sheet.getLastRowNum();
根據下標獲取指定行
Row row = sheet.getRow(0);
遍歷所有行
for(Row row : sheet) { System.out.println(row); }
遍歷指定區域行
for (int i = 1; i <= sheet.getLastRowNum(); i++) { Row row = sheet.getRow(i); System.out.println(row); }
4.Cell(單元格)
通過 Row 來進行單元格 Cell 對象的獲取或創建。
創建單元格
Cell cell0 = row.createCell(0);
設置單元格值
cell0.setCellValue(UUID.randomUUID().toString());
根據下標獲取單元格
Cell cell = row.getCell(1);
遍歷所有單元格
for(Cell cell : row) {}
獲取單元格的類型
CellType type = cell.getCellType();
設置單元格樣式
// 創建單元格樣式 DataFormat dataFormat = workbook.createDataFormat(); Short formatCode = dataFormat.getFormat("yyyy-MM-dd HH:mm:ss"); CellStyle cellStyle = workbook.createCellStyle(); cellStyle.setDataFormat(formatCode); // 為當前行創建單元格 Cell cell1 = row.createCell(1); cell1.setCellStyle(cellStyle); // 設置單元格樣式 cell1.setCellValue(new Date()); // 保存當前日期時間至本單元格
設置單元格對齊
// 創建單元格樣式 CellStyle cellStyle = workbook.createCellStyle(); //設置單元格的水平對齊類型。 此時水平居中 cellStyle.setAlignment(HorizontalAlignment.CENTER); // 設置單元格的垂直對齊類型。 此時垂直靠底邊 cellStyle.setVerticalAlignment(VerticalAlignment.BOTTOM);
四、超大Excel文件讀寫
1.使用POI寫入
使用 SXSSFWorkbook 進行寫入,通過設置 SXXFWorkbook 的構造參數,可以設置每次在內存中保持的行 數,當達到這個值的時候,那麼會把這些數據 flush 到磁盤上,這樣就不會出現內存不夠的情況。
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時POI會把文件的所有內容都加載到內存中,很容易占用大量內存;甚至發生out of memory異常。
2.使用EasyExcel
- Java領域解析、生成Excel比較有名的框架有Apache poi、jxl等。但他們都存在一個嚴重的問題就是非常的耗內存。如果你的系統並發量不大的話可能還行,但是一旦並發上來後一定會OOM或者JVM頻繁的full gc。
- EasyExcel是阿裡巴巴開源的一個excel處理框架,以使用簡單、節省內存著稱。EasyExcel能大大減少占用內存的主要原因是在解析Excel時沒有將文件數據一次性全部加載到內存中,而是從磁盤上一行行讀取數據,逐個解析。
- EasyExcel采用一行一行的解析模式,並將一行的解析結果以觀察者的模式通知處理。
例:
//準備實體類 public class Order { @ExcelProperty("訂單編號") private String orderId; // 訂單編號 @ExcelProperty("支付金額") @NumberFormat("¥#,###") private Double payment; // 支付金額 @ExcelProperty(value = "創建日期",converter = LocalDateTimeConverter.class) private LocalDateTime creationTime; // 創建時間 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 + "]"; } }
//準備Converter轉換類 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"))); } }
寫入數據
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("共耗時"+(end-begin)+"毫秒"); } // 創建100w條訂單數據 private static List<Order> data() { List<Order> list = new ArrayList<Order>(); for (int i = 0; i < 1000000; i++) { list.add(new Order()); } return list; } }
讀取數據
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) { //用於保存讀取到的結果 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) { // 讀取每條數據 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); } } }
到此這篇關於Java操作Excel文件解析與讀寫方法詳解的文章就介紹到這瞭,更多相關Java Excel文件解析內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!
推薦閱讀:
- Java實現超大Excel文件解析(XSSF,SXSSF,easyExcel)
- 詳解Python操作Excel之openpyxl
- Java使用EasyExcel進行單元格合並的問題詳解
- Java+EasyExcel實現文件的導入導出
- Java easyExcel的復雜表頭多級表頭導入