產生原因:
因爲在poi的用戶模式中,都是使用批量處理數據,也就是說導入時,將文檔所有數據先讀取存放在內存中,待完全讀取完畢之後再進行導入操作(期間內存被佔用的資源不可釋放,垃圾回收機制也是無法進行資源釋放的),導出亦如此。
導致後果:
內存不斷被佔用,如果在數據導出/導入的過程中,內存資源被完全佔用,則會出現內存溢出,嚴重則可能使項目崩潰
解決方案:
導出:
- 減少對象產生(不創建額外的樣式和字體)
- 用SXSSFWorkbook進行poi工作簿的創建(後續操作和普通 XSSFWorkbook使用相同),則會將閾值溢出的數據先保存到磁盤,待所有數據處理完成之後,一同合併到下載流中,進行數據導出並下載。
//設置時間,用作報表名字和員工查詢條件
String month = "2020-1";
//獲取數據 (根據自己情況修改)
List<EmployeeReportResult> list = userCompanyPersonalService.findByRepost(companyId,month);
//構造excel
//創建工作簿
SXSSFWorkbook workbook = new SXSSFWorkbook(100);
//創建sheet
Sheet sheet = workbook.createSheet();
//構造標題 (根據自己情況修改)
String[] titles = {"編號", "姓名", "手機","最高學歷", "國家地區", "護照號", "籍貫", "生日", "屬相","入職時間","離職類型","離職原因","離職時間"};
//寫入標題
//創建行
Row row = sheet.createRow(0);
int cellNum = 0;
for (String title : titles) {
//創建單元格
Cell cell = row.createCell(cellNum);
cell.setCellValue(title);
cellNum ++;
}
//寫入內容 (根據自己情況修改)
AtomicInteger headersAi = new AtomicInteger();
for(int i = 0; i<100000; i ++){
Cell cell = null;
for (EmployeeReportResult result : list) {
//創建單元格
Row dataRow = sheet.createRow(headersAi.getAndIncrement());
//編號
cell = dataRow.createCell(0);
cell.setCellValue(result.getUserId());
//姓名
cell = dataRow.createCell(1);
cell.setCellValue(result.getUsername());
//手機
cell = dataRow.createCell(2);
cell.setCellValue(result.getMobile());
//最高學歷
cell = dataRow.createCell(3);
cell.setCellValue(result.getTheHighestDegreeOfEducation());
//國家地區
cell = dataRow.createCell(4);
cell.setCellValue(result.getNationalArea());
//護照號
cell = dataRow.createCell(5);
cell.setCellValue(result.getPassportNo());
//籍貫
cell = dataRow.createCell(6);
cell.setCellValue(result.getNativePlace());
//生日
cell = dataRow.createCell(7);
cell.setCellValue(result.getBirthday());
//屬相
cell = dataRow.createCell(8);
cell.setCellValue(result.getZodiac());
//入職時間
cell = dataRow.createCell(9);
cell.setCellValue(result.getTimeOfEntry());
//離職類型
cell = dataRow.createCell(10);
cell.setCellValue(result.getTypeOfTurnover());
//離職原因
cell = dataRow.createCell(11);
cell.setCellValue(result.getReasonsForLeaving());
//離職時間
cell = dataRow.createCell(12);
cell.setCellValue(result.getResignationTime());
}
}
//通過輸出流進行文件下載
String name = month+"_人事報表.xlsx";
ServletOutputStream out = response.getOutputStream();
//指定格式
response.setContentType("application/vnd.ms-excel");
response.setHeader("content-Disposition", "attachment;filename=report.xlsx");
workbook.write(out);
out.flush();
out.close();
導入:
- 使用Sax事件處理器,每獲取一行文檔,則將數據讀取到,觸發處理器,將已讀的數據從內存中刪除。
- 自定義Sax的解析處理器
//自定義Sheet基於Sax的解析處理器
public class SheetHandler implements XSSFSheetXMLHandler.SheetContentsHandler {
//封裝實體對象
private PoiEntity entity;
/**
* 解析行開始
*/
@Override
public void startRow(int rowNum) {
if (rowNum >0 ) {
entity = new PoiEntity();
}
}
/**
* 解析每一個單元格
*/
@Override
public void cell(String cellReference, String formattedValue, XSSFComment comment)
{
if(entity != null) {
//判斷單元格名稱,Excel上面的A.B.C.....標題
switch (cellReference.substring(0, 1)) {
case "A":
entity.setId(formattedValue);
break;
case "B":
entity.setBreast(formattedValue);
break;
case "C":
entity.setAdipocytes(formattedValue);
break;
case "D":
entity.setNegative(formattedValue);
break;
case "E":
entity.setStaining(formattedValue);
break;
case "F":
entity.setSupportive(formattedValue);
break;
default:
break;
}
}
}
/**
* 解析行結束
*/
public void endRow(int rowNum) {
System.out.println(entity);
}
//處理頭尾
public void headerFooter(String text, boolean isHeader, String tagName) {
}
}
- 自定義Excel解析器
/**
* 自定義Excel解析器
*/
public class ExcelParser {
public void parse(String path) throws Exception {
//1.根據Excel獲取OPCPackage對象
OPCPackage pkg = OPCPackage.open(path, PackageAccess.READ);
try {
//2.創建XSSFReader對象
XSSFReader reader = new XSSFReader(pkg);
//3.獲取SharedStringsTable對象
SharedStringsTable sst = reader.getSharedStringsTable();
//4.獲取StylesTable對象
StylesTable styles = reader.getStylesTable();
//5.創建Sax的XmlReader對象
XMLReader parser = XMLReaderFactory.createXMLReader();
//6.設置處理器
parser.setContentHandler(new XSSFSheetXMLHandler(styles, sst, new
SheetHandler(), false));
XSSFReader.SheetIterator sheets = (XSSFReader.SheetIterator)
reader.getSheetsData();
//7.逐行讀取
while (sheets.hasNext()) {
InputStream sheetstream = sheets.next();
InputSource sheetSource = new InputSource(sheetstream);
try {
parser.parse(sheetSource);
} finally {
sheetstream.close();
}
}
} finally {
pkg.close();
}
}
}