EasyExcel是一款處理excel的工具類,主要特點如下(官方):
特點
- 高性能讀寫:FastExcel 專注于性能優化,能夠高效處理大規模的 Excel 數據。相比一些傳統的 Excel 處理庫,它能顯著降低內存占用。
- 簡單易用:該庫提供了簡潔直觀的 API,使得開發者可以輕松集成到項目中,無論是簡單的 Excel 操作還是復雜的數據處理都能快速上手。
- 流式操作:FastExcel 支持流式讀取,將一次性加載大量數據的問題降到最低。這種設計方式在處理數十萬甚至上百萬行的數據時尤為重要。
我自己實踐之后的感受如下:
- 性能比較好,底層使用SXSSF,在大數據的情況下,會先往硬盤中插入,加快速度。(SXSSF 通過將數據寫入磁盤而不是全部保留在內存中,來減少內存的使用。這種 “流式” 寫入方式特別適合處理那些可能導致傳統內存處理方式崩潰的大型 CSV 文件。SXSSF 可以在寫入數據的同時,將數據保存在一個臨時的文件中,這樣即使處理非常大的數據集,內存的使用也會保持在可控范圍內。)
- 支持注解,可以在每個字段屬性上添加注解,可以設置表格的表頭/顏色/字體/合并單元格等屬性。
- 支持策略,針對復雜的表格,支持注入策略,針對表格進行各種加工。
- 支持excel轉pdf(僅支持xlsx,版本為1.0.0)
實踐
引入依賴
<dependency><groupId>cn.idev.excel</groupId><artifactId>fastexcel</artifactId><version>1.1.0</version>
</dependency>
1.表頭支持注解方式生成
比如,下方的示例:創建一個綠色背景,加邊框的表頭
代碼的話可以直接這樣寫:
新增一個訂單類:
@Data
// 表頭背景設置
@HeadStyle(fillPatternType = FillPatternTypeEnum.SOLID_FOREGROUND, fillForegroundColor = 17)
public class Order {@ExcelProperty("訂單號")private String orderId;@ExcelProperty("渠道")private String searchChannel;@ExcelProperty("創建時間")private String createTime;@ExcelProperty("乘客姓名")private String name;@ExcelProperty("證件號")private String idCard;
}
創建表格
/*** 合并單元格* <p>* 1. 創建excel對應的實體對象 參照{@link DemoData} {@link DemoMergeData}* <p>* 2. 創建一個merge策略 并注冊* <p>* 3. 直接寫即可** @since 2.2.0-beta1*/
@Test
public void mergeOrderWrite() {//使用策略合并單元格try (FileOutputStream excel = new FileOutputStream("mergeOrderWrite.xlsx");BufferedOutputStream bos = new BufferedOutputStream(excel)) {// 這里 需要指定寫用哪個class去寫,然后寫到第一個sheet,名字為模板 然后文件流會自動關閉EasyExcel.write(bos, Order.class).sheet("模板").doWrite(buildData());System.out.println("導出完成");} catch (IOException e) {System.out.println("導出失敗:" + e.getMessage());} catch (Exception e) {throw new RuntimeException(e);}
}//初始化數據
private List<Order> buildData() {List<Order> list = new ArrayList<>();for (int i = 0; i < 100; i++) {for (int j = 0; j < 2; j++) {Order user = new Order();user.setOrderId("orderId_" + i);user.setSearchChannel("searchChannel_" + i);user.setCreateTime(DateTimeUtil.nowString());user.setName("name_" + j);user.setIdCard("idCard_" + j);list.add(user);}}Order user1 = new Order();user1.setOrderId("orderId_99");user1.setSearchChannel("searchChannel_99");user1.setCreateTime(DateTimeUtil.nowString());user1.setName("name_a");user1.setIdCard("idCard_a");list.add(user1);return list;
}
上面代碼執行之后,即可以生成效果圖中的表格數據
2.通過添加策略,來支持復雜的表格處理
比如我們要處理一個表格,里面的數據涉及到動態合并單元格。需要將相鄰的相同訂單號合并到一起
支持添加策略實現:
思路:遍歷每個row,如果兩個orderid一樣,則合并
@Slf4j
public class LoopOrderMergeStrategy implements RowWriteHandler {/*** 訂單號*/private String orderId;/*** 當前訂單號起始坐標*/private Integer indexStart;/*** 合并策略,訂單號和創建時間合并** @author zhouxy* @date 2025/4/3 17:27*/public void afterRowDispose(RowWriteHandlerContext context) {log.info("進來,,,{},{}", context.getRow().getRowNum(), context.getRelativeRowIndex());//遍歷每個row,如果兩個orderid一樣,則合并//這里是獲取每一行數據Cell cell = context.getRow().getCell(0);String currentOrderId = cell.getStringCellValue();if (Strings.isEmpty(this.orderId)) {this.orderId = currentOrderId;indexStart = context.getRowIndex();} else if (Objects.equals(this.orderId, currentOrderId)) {//合并單元格//訂單號CellRangeAddress orderCellRangeAddress = new CellRangeAddress(indexStart,context.getRowIndex(),cell.getColumnIndex(),cell.getColumnIndex());context.getWriteSheetHolder().getSheet().addMergedRegionUnsafe(orderCellRangeAddress);//渠道Cell searchChannelCell = context.getRow().getCell(1);CellRangeAddress searchChannelCellRangeAddress = new CellRangeAddress(indexStart,context.getRowIndex(),searchChannelCell.getColumnIndex(),searchChannelCell.getColumnIndex());context.getWriteSheetHolder().getSheet().addMergedRegionUnsafe(searchChannelCellRangeAddress);//創建時間Cell createtimeCell = context.getRow().getCell(2);CellRangeAddress createTimeCellRangeAddress = new CellRangeAddress(indexStart,context.getRowIndex(),createtimeCell.getColumnIndex(),createtimeCell.getColumnIndex());context.getWriteSheetHolder().getSheet().addMergedRegionUnsafe(createTimeCellRangeAddress);} else if (!Objects.equals(this.orderId, currentOrderId)) {this.orderId = currentOrderId;indexStart = context.getRowIndex();}}}
生成表格的時候,將該策略設置進去
/*** 合并單元格* <p>* 1. 創建excel對應的實體對象 參照{@link DemoData} {@link DemoMergeData}* <p>* 2. 創建一個merge策略 并注冊* <p>* 3. 直接寫即可** @since 2.2.0-beta1*/
@Test
public void mergeOrderWrite() {//使用策略合并單元格try (FileOutputStream excel = new FileOutputStream("mergeOrderWrite.xlsx");BufferedOutputStream bos = new BufferedOutputStream(excel)) {LoopOrderMergeStrategy loopMergeStrategy = new LoopOrderMergeStrategy();// 這里 需要指定寫用哪個class去寫,然后寫到第一個sheet,名字為模板 然后文件流會自動關閉EasyExcel.write(bos, Order.class).registerWriteHandler(loopMergeStrategy).sheet("模板").doWrite(buildData());System.out.println("導出完成");} catch (IOException e) {System.out.println("導出失敗:" + e.getMessage());} catch (Exception e) {throw new RuntimeException(e);}
}
3.支持excel轉pdf,但是只有1.0.0版本有,新版本去掉了。
@Testpublic void excelToPdf() {FastExcel.convertToPdf(new File("excel1.xlsx"),new File("pdfFile"),null,null);}
4.性能
相同數據生成用時比較:ExsyExcel > SXSSF > HSSF. 但是HSSF最大行數為65565行,再大就超出范圍報錯
HSSF可寫入行數
easyexcel和SXSSF可寫入行數
綜合比較用時的話,,EasyExcel和SX SSF的用時差不多,HSSF的用時相當于前兩者的三分之一。但是HSSF數據量支持較小,但是綜合下來的話,從用時,代碼簡潔度來說,建議使用EasyExcel
其他功能可以看官方的github,里面有很多示例。比如支持轉圖片,根據模板寫入excel等。