實現萬行級excel匯出---poi--ooxm的應用和採坑
xl_echo編輯整理,歡迎轉載,轉載請宣告文章來源。歡迎新增echo微信(微訊號:t2421499075)交流學習。 百戰不敗,依不自稱常勝,百敗不頹,依能奮力前行。——這才是真正的堪稱強大!!
閱讀建議:如果系統沒有做過相關匯入匯出,又需要這個量級的可以直接閱讀底部的poi-ooxm的應用
excel使用poi直接匯出最大值是多少?估計很多人不會關注這個問題,因為很少有業務要求excel直接匯出上萬條資料,更甚者可能沒有聽過excel直接匯出100w條資料。這裡給大家介紹一個引用場景和博主採坑的經歷。
需求描述
公司要求實現一個銀行流水的匯出功能,看上去就是一個簡單的按鈕,但是這個需求有幾點要求:
- 匯出的檔案格式需要為xlsx
- 每次匯出最低量為5w條,且為一個表
- 匯出的時候需要以檔案下載的形式在瀏覽器下載
樓主開發環境:jdk1.8, idea2018.1,springboot1.5x,dubbox
對於poi-3.9的一次嘗試
剛開始的時候,沒有過多的關注5w條這個數量,直接使用的poi-3.9。在整個開發過程中基本沒有碰到問題。在測試的時候,碰到了一個不能滿足需求的問題。當我直接下載5w條的時候,程式直接報錯。經過不斷的測試,發現3.9的直接使用,最高下載值為6000+(這個最高值和電腦效能有一定的關係,不過出入不會很大)。
測試結論poi-3.9的天花板 6000+
很明顯上面的嘗試不能做出與需求相關的功能,經過百度,發現easyexcel是一個不錯的解決方案。
對於easyexcel的一次嘗試
匯入的easyexcel為
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>1.1.1</version>
</dependency>
使用該依賴的時候,我參考了以為博主的程式碼,程式碼地址為:https://blog.csdn.net/qq_35206261/article/details/88579151。當我將他的百萬行級別的解決方案搬到我的專案中的時候,發現一切好像沒有問題。但是當我啟動的時候發現一直報錯java.lang.NoSuchMethodError
引入的依賴如下:
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>3.9</version>
</dependency>
衝突如下圖所示:
坑一poi-ooxml沒有向下相容
點選進入easyexcel的依賴發現,它的底層是依賴的poi3.17的版本。當我一直以為poi是能夠向下相容的時候,最終每次啟動和編譯的時候出現的問題都指向了並沒有向下相容。 底層的poi-ooxml版本為3.17,如圖所示
這個時候我們可以如果還要去使用easyexcel,那我們需要更改版本或者解決poi-ooxml的版本相容問題。
經過不相容問題之後,看公司原有poi-ooxml的應用於是決定使用poi-ooxml的解決方案。
poi-ooxml的應用
經過以上幾個坑之後,於是決定使用poi-ooxml的3.9版本能夠相容的解決方案,百度之後發現有一個以3.8版本基礎的應用。經過上面的不相容之後,還是決定試一試。於是採用了該博主文章中的一段程式碼,文章地址:https://blog.csdn.net/happyljw/article/details/52809244。
這篇文章中描述了一個思路,同時也給出了一段博主提供的實現程式碼,相對來說,如果作為測試問題不大,最後根據需求進行調整,修改了部分實現的步驟,同時也新增了一些新的實現和限制。程式碼如下:
@ResponseBody
@RequestMapping(value = "/exportDataMoreThan1000")
public void readMoreThan1000RowBySheet(@RequestParam(value = "start") Integer start,
@RequestParam(value = "limit") Integer limit,
HttpServletResponse response) throws Exception {
//記憶體中只建立100個物件,寫臨時檔案,當超過100條,就將記憶體中不用的物件釋放。
Workbook wb = new SXSSFWorkbook(100);
//工作表物件
final Sheet[] sheet = {null};
//行物件
final Row[] nRow = {null};
//列物件
final Cell[] nCell = {null};
//總行號
final int[] rowNo = {0};
//頁行號
final int[] pageRowNo = {0};
List<BankDto> list = new ArrayList<>(50000);
//資料來源
list = BankServer.getList();
list.forEach(it -> {
if (rowNo[0] % 10001 == 0) {
sheet[0] = wb.createSheet("我的第" + (rowNo[0] / 10001 + 1) + "個工作簿");
sheet[0] = wb.getSheetAt(rowNo[0] / 10001);
//每當新建了工作表就將當前工作表的行號重置為0
pageRowNo[0] = 0;
}
rowNo[0]++;
nRow[0] = sheet[0].createRow(pageRowNo[0]++);
//這一步很關鍵,如果沒有就沒有表頭。
if (pageRowNo[0] == 1) {
for (int j = 0; j < 9; j++) {
nCell[0] = nRow[0].createCell(j);
if (j == 0) nCell[0].setCellValue("編號");
if (j == 1) nCell[0].setCellValue("編號");
if (j == 2) nCell[0].setCellValue("編號");
if (j == 3) nCell[0].setCellValue("編號");
if (j == 4) nCell[0].setCellValue("編號");
if (j == 5) nCell[0].setCellValue("編號");
if (j == 6) nCell[0].setCellValue("編號");
if (j == 7) nCell[0].setCellValue("編號");
if (j == 8) nCell[0].setCellValue("備註");
}
rowNo[0]++;
nRow[0] = sheet[0].createRow(pageRowNo[0]++);
}
// 輸出每行,每行有9列資料
for (int j = 0; j < 9; j++) {
nCell[0] = nRow[0].createCell(j);
if (j == 0) nCell[0].setCellValue(it.getPaycode());
if (j == 1) nCell[0].setCellValue(it.getPaycode());
if (j == 2) nCell[0].setCellValue(it.getPaycode());
if (j == 3) nCell[0].setCellValue(it.getPaycode());
if (j == 4) nCell[0].setCellValue(it.getPaycode());
if (j == 5) nCell[0].setCellValue(it.getPaycode());
if (j == 6) nCell[0].setCellValue(it.getPaycode());
if (j == 7) nCell[0].setCellValue(it.getPaycode());
if (j == 8) nCell[0].setCellValue(it.getRemark());
}
});
String fileName = "銀行流水錶.xlsx";
//設定請求頭
response.setHeader("content-Type", "application/vnd.ms-excel");
response.setContentType("application/vnd.ms-excel;charset=utf-8");
response.setHeader("Content-Disposition", "attachment; filename=" + fileName + ".xlsx");
ServletOutputStream outputStream = response.getOutputStream();
wb.write(outputStream);
outputStream.flush();
outputStream.close();
}
這裡簡化了資料來源的操作,如果需要可以根據自己的業務進行修改,我的實際實現裡對資料來源操作相對複查,不僅進行了分片請求(避免dubbox超時),同時還對資料進行了很多處理。這裡的操作有一個亮點,那就是進行了多Sheet的分割。
注意:這裡的開發環境是jdk1.8
當完成以上的程式碼編寫之後,使用工具測試,發現已經實現了我需要的功能。目前的一個下載量10w以內都沒有什麼太大的問題,實測10w資料20s。如果業務邏輯簡單些還會提升一倍的速度。
總結:
- 萬行級的解決方案有兩種
-
- poi-ooxml
-
- easyexc
- 如果使用其中的某一種要注意是否引入了另外一種,可能會不相容。
- poi-ooxml3.9和poi-ooxml3.17不能完美向下相容
- poi3.17最大下