百萬級EXCEL匯出
阿新 • • 發佈:2018-12-11
一. 簡介
excel匯出,如果資料量在百萬級,會出現倆點記憶體溢位的問題:
1. 查詢資料量過大,導致記憶體溢位。 該問題可以通過分批查詢來解決;
2. 最後下載的時候大EXCEL轉換的輸出流記憶體溢位;該方式可以通過新版的SXSSFWorkbook來解決,可通過其建構函式執指定在記憶體中快取的行數,剩餘的會自動快取在硬碟的臨時目錄上;
3. 為了能夠使用不同的mapper並分批寫資料, 採用了模板方法設計模式,在可變的匿名內部類中實現寫入邏輯。
二. 工具程式碼
2.1 pom.xml
<!-- poi --> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi</artifactId> <version>3.17</version> </dependency> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-ooxml</artifactId> <version>3.17</version> </dependency>
注: 如果是springboot2.0,則不需要poi依賴,如果是1.0,則需要poi依賴,並且poi和poi-ooxml的版本要保持一致。
別的依賴我就不加了。
2.2 ExcelConstant
package com.yzx.caasscs.constant; /** * @author qjwyss * @date 2018/9/19 * @description EXCEL常量類 */ public class ExcelConstant { /** * 每個sheet儲存的記錄數 100W */ public static final Integer PER_SHEET_ROW_COUNT = 1000000; /** * 每次向EXCEL寫入的記錄數(查詢每頁資料大小) 20W */ public static final Integer PER_WRITE_ROW_COUNT = 200000; /** * 每個sheet的寫入次數 5 */ public static final Integer PER_SHEET_WRITE_COUNT = PER_SHEET_ROW_COUNT / PER_WRITE_ROW_COUNT; }
注: xlsx模式的excel每個sheet最多儲存104W,此處我就每個sheet儲存了 100W資料;每次查詢20W資料;
2.3 寫資料委託類
package com.yzx.caasscs.util; import org.apache.poi.xssf.streaming.SXSSFSheet; /** * @author qjwyss * @date 2018/9/20 * @description EXCEL寫資料委託類 */ public interface WriteExcelDataDelegated { /** * EXCEL寫資料委託類 針對不同的情況自行實現 * * @param eachSheet 指定SHEET * @param startRowCount 開始行 * @param endRowCount 結束行 * @param currentPage 分批查詢開始頁 * @param pageSize 分批查詢資料量 * @throws Exception */ public abstract void writeExcelData(SXSSFSheet eachSheet, Integer startRowCount, Integer endRowCount, Integer currentPage, Integer pageSize) throws Exception; }
2.4 POI工具類
package com.yzx.caasscs.util;
import com.yzx.caasscs.constant.ExcelConstant;
import org.apache.poi.xssf.streaming.SXSSFCell;
import org.apache.poi.xssf.streaming.SXSSFRow;
import org.apache.poi.xssf.streaming.SXSSFSheet;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.servlet.http.HttpServletResponse;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Date;
/**
* @author qjwyss
* @date 2018/9/18
* @description POI匯出工具類
*/
public class PoiUtil {
private final static Logger logger = LoggerFactory.getLogger(PoiUtil.class);
/**
* 初始化EXCEL(sheet個數和標題)
*
* @param totalRowCount 總記錄數
* @param titles 標題集合
* @return XSSFWorkbook物件
*/
public static SXSSFWorkbook initExcel(Integer totalRowCount, String[] titles) {
// 在記憶體當中保持 100 行 , 超過的資料放到硬碟中在記憶體當中保持 100 行 , 超過的資料放到硬碟中
SXSSFWorkbook wb = new SXSSFWorkbook(100);
Integer sheetCount = ((totalRowCount % ExcelConstant.PER_SHEET_ROW_COUNT == 0) ?
(totalRowCount / ExcelConstant.PER_SHEET_ROW_COUNT) : (totalRowCount / ExcelConstant.PER_SHEET_ROW_COUNT + 1));
// 根據總記錄數建立sheet並分配標題
for (int i = 0; i < sheetCount; i++) {
SXSSFSheet sheet = wb.createSheet("sheet" + (i + 1));
SXSSFRow headRow = sheet.createRow(0);
for (int j = 0; j < titles.length; j++) {
SXSSFCell headRowCell = headRow.createCell(j);
headRowCell.setCellValue(titles[j]);
}
}
return wb;
}
/**
* 下載EXCEL到本地指定的資料夾
*
* @param wb EXCEL物件SXSSFWorkbook
* @param exportPath 匯出路徑
*/
public static void downLoadExcelToLocalPath(SXSSFWorkbook wb, String exportPath) {
FileOutputStream fops = null;
try {
fops = new FileOutputStream(exportPath);
wb.write(fops);
} catch (Exception e) {
e.printStackTrace();
} finally {
if (null != wb) {
try {
wb.dispose();
} catch (Exception e) {
e.printStackTrace();
}
}
if (null != fops) {
try {
fops.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
/**
* 下載EXCEL到瀏覽器
*
* @param wb EXCEL物件XSSFWorkbook
* @param response
* @param fileName 檔名稱
* @throws IOException
*/
public static void downLoadExcelToWebsite(SXSSFWorkbook wb, HttpServletResponse response, String fileName) throws IOException {
response.setHeader("Content-disposition", "attachment; filename="
+ new String((fileName + ".xlsx").getBytes("utf-8"), "ISO8859-1"));//設定下載的檔名
OutputStream outputStream = null;
try {
outputStream = response.getOutputStream();
wb.write(outputStream);
} catch (Exception e) {
e.printStackTrace();
} finally {
if (null != wb) {
try {
wb.dispose();
} catch (Exception e) {
e.printStackTrace();
}
}
if (null != outputStream) {
try {
outputStream.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
/**
* 匯出Excel到本地指定路徑
*
* @param totalRowCount 總記錄數
* @param titles 標題
* @param exportPath 匯出路徑
* @param writeExcelDataDelegated 向EXCEL寫資料/處理格式的委託類 自行實現
* @throws Exception
*/
public static final void exportExcelToLocalPath(Integer totalRowCount, String[] titles, String exportPath, WriteExcelDataDelegated writeExcelDataDelegated) throws Exception {
logger.info("開始匯出:" + DateUtil.formatDate(new Date(), DateUtil.YYYY_MM_DD_HH_MM_SS));
// 初始化EXCEL
SXSSFWorkbook wb = PoiUtil.initExcel(totalRowCount, titles);
// 呼叫委託類分批寫資料
int sheetCount = wb.getNumberOfSheets();
for (int i = 0; i < sheetCount; i++) {
SXSSFSheet eachSheet = wb.getSheetAt(i);
for (int j = 1; j <= ExcelConstant.PER_SHEET_WRITE_COUNT; j++) {
int currentPage = i * ExcelConstant.PER_SHEET_WRITE_COUNT + j;
int pageSize = ExcelConstant.PER_WRITE_ROW_COUNT;
int startRowCount = (j - 1) * ExcelConstant.PER_WRITE_ROW_COUNT + 1;
int endRowCount = startRowCount + pageSize - 1;
writeExcelDataDelegated.writeExcelData(eachSheet, startRowCount, endRowCount, currentPage, pageSize);
}
}
// 下載EXCEL
PoiUtil.downLoadExcelToLocalPath(wb, exportPath);
logger.info("匯出完成:" + DateUtil.formatDate(new Date(), DateUtil.YYYY_MM_DD_HH_MM_SS));
}
/**
* 匯出Excel到瀏覽器
*
* @param response
* @param totalRowCount 總記錄數
* @param fileName 檔名稱
* @param titles 標題
* @param writeExcelDataDelegated 向EXCEL寫資料/處理格式的委託類 自行實現
* @throws Exception
*/
public static final void exportExcelToWebsite(HttpServletResponse response, Integer totalRowCount, String fileName, String[] titles, WriteExcelDataDelegated writeExcelDataDelegated) throws Exception {
logger.info("開始匯出:" + DateUtil.formatDate(new Date(), DateUtil.YYYY_MM_DD_HH_MM_SS));
// 初始化EXCEL
SXSSFWorkbook wb = PoiUtil.initExcel(totalRowCount, titles);
// 呼叫委託類分批寫資料
int sheetCount = wb.getNumberOfSheets();
for (int i = 0; i < sheetCount; i++) {
SXSSFSheet eachSheet = wb.getSheetAt(i);
for (int j = 1; j <= ExcelConstant.PER_SHEET_WRITE_COUNT; j++) {
int currentPage = i * ExcelConstant.PER_SHEET_WRITE_COUNT + j;
int pageSize = ExcelConstant.PER_WRITE_ROW_COUNT;
int startRowCount = (j - 1) * ExcelConstant.PER_WRITE_ROW_COUNT + 1;
int endRowCount = startRowCount + pageSize - 1;
writeExcelDataDelegated.writeExcelData(eachSheet, startRowCount, endRowCount, currentPage, pageSize);
}
}
// 下載EXCEL
PoiUtil.downLoadExcelToWebsite(wb, response, fileName);
logger.info("匯出完成:" + DateUtil.formatDate(new Date(), DateUtil.YYYY_MM_DD_HH_MM_SS));
}
}