产生原因:
因为在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();
}
}
}