问题描述

操作员在导入Excel文件时发生了OOM(文件的数据3w行 * 60列),发生了OOM,jvm的运行内存1G

领导要求必须解决这个问题。

正在运行的代码

// 仅2007版本的解析

Workbook workbook = new XSSFWorkbook(inputStream);

Sheet sheet = workbook.getSheetAt(0);

for (int rowIndex = 1; rowIndex < rowCount; rowIndex++) {

Row row = sheet.getRow(rowIndex);

if (Objects.nonNull(row)) {

int cellCount = row.getPhysicalNumberOfCells();

for (int i = 0; i < cellCount; i++) {

Cell cell = getCell(row, i);

String value = cell.getStringCellValue();

// ..... 读取的值处理

}

}

}

看着代码好像没有啥问题,百度下大家都是这么写的。。。。

解决问题

研究下可以怎么优化

有问题还得靠百度或CSDN,找到很多的关于导入的优化,我认为讲的比较清楚的文章贴出来给大家参考!!!

Excel大批量导入导出解决方案

通过这篇文章详细了解了POI对导入分为3种模式,用户模式User Model,事件模式Event Model,还有Event User Model。 了解了导入的原理后,就知道怎么优化这个问题了,因为我们只考虑xlsx格式的文件导入,所以定位优化点改为POI的Event User Model解析。

如果你不想看官方的例子,就直接看我的改造代码好了

优化后的代码

Maven依赖不用修改

读取Excel文件流并解析文件

try (final OPCPackage pck = OPCPackage.open(inputStream)) {

final XSSFReader reader = new XSSFReader(pck);

final StylesTable stylesTable = reader.getStylesTable();

final Iterator sheets = reader.getSheetsData();

final ReadOnlySharedStringsTable rsst = new ReadOnlySharedStringsTable(pck);

// 存储解析的所有行对象

List result = new LinkedList<>();

// 重要的是这个文件的实现SheetHandler

final SheetHandler contentHandler = new SheetHandler(rowClass, result, getFirstDataRowIndex());

final XSSFSheetXMLHandler handler = new XSSFSheetXMLHandler(stylesTable, rsst, contentHandler, new MyDataFormatter(), false);

while (sheets.hasNext()) {

final InputStream sheet = sheets.next();

final InputSource sheetSource = new InputSource(sheet);

final XMLReader xmlReader = XMLReaderFactory.createXMLReader("org.apache.xerces.parsers.SAXParser");

xmlReader.setContentHandler(handler);

xmlReader.parse(sheetSource);

sheet.close();

}

// 后面可以进行校验数据 ObjectValidationHolder.verify(),后面有时间再多放些后续的处理代码逻辑

return result;

}

SheetHandler.java 实现 重点关注 带注解@Override 的方法

import lombok.extern.slf4j.Slf4j;

import org.apache.commons.lang3.StringUtils;

import org.apache.poi.xssf.eventusermodel.XSSFSheetXMLHandler;

import org.apache.poi.xssf.usermodel.XSSFComment;

import org.df.excel.constants.EnumHelper;

import org.df.excel.constants.IEnum;

import org.df.excel.dto.BaseImportDTO;

import org.df.excel.upload.excel.annoation.Cell;

import org.df.excel.util.bean.ReflectUtil;

import org.springframework.util.ReflectionUtils;

import java.lang.reflect.Field;

import java.util.List;

import java.util.Map;

import java.util.Objects;

/**

* 读取一个sheet的处理器

* 循环遍历读取每一行数据

*

* @author ff

* @date 2022/8/30 14:29

*/

@Slf4j

// 如果你不需要记录行索引,T 可以不继承任何基类

public class SheetHandler implements XSSFSheetXMLHandler.SheetContentsHandler {

/**

* 转换后的每行数据JavaBean实例

*/

private T rowBean;

/**

* 行数据的JavaBean类型

*/

private final Class rowClass;

/**

* 读取sheet中所有的数据,存储的list

*/

private final List dataList;

/**

* 第一行数据的行索引。从0开始

*/

private final int firstDataRowIndex;

/**

* 行数据映射的Java类中所有的字段的集合Map

* Key: 列头A,B,C....

* Value: 对应Java类中的字段名

*/

private final Map rowBeanFieldMap;

public SheetHandler(Class rowClass, List dataList, int firstDataRowIndex) {

this.rowClass = rowClass;

this.dataList = dataList;

this.firstDataRowIndex = firstDataRowIndex;

this.rowBeanFieldMap = ReflectUtil.getFieldMap(rowClass);

}

/**

* 开始读一行的数据

*

* @param rowIndex

*/

@Override

public void startRow(int rowIndex) {

if (rowIndex >= firstDataRowIndex) {

try {

this.rowBean = this.rowClass.newInstance();

// 如果你不需要记录行索引,可以不继承任何基类

rowBean.setRowIndex(rowIndex);

} catch (ReflectiveOperationException e) {

e.printStackTrace();

}

}

}

/**

* 行数据读取结束

*

* @param rowIndex

*/

@Override

public void endRow(int rowIndex) {

if (Objects.nonNull(this.rowBean)) {

this.dataList.add(this.rowBean);

}

this.rowBean = null;

}

/**

* 读一个单元格的值

*

* @param cellAddress 单元格地址(A1,V3,C3....)

* @param cellValue 单元格的值

* @param xssfComment

*/

@Override

public void cell(String cellAddress, String cellValue, XSSFComment xssfComment) {

if (Objects.isNull(rowBean) || StringUtils.isBlank(cellValue)) {

return;

}

Field field = getField(cellAddress, this.rowBeanFieldMap);

try {

if (Objects.nonNull(field)) {

field.setAccessible(true);

Object fieldValue = getCellValue(cellValue, field);

ReflectionUtils.setField(field, rowBean, fieldValue);

}

} catch (Exception e) {

log.error("字段[{}]映射的单元格[{}]取值异常", field.getName(), cellAddress, e);

}

}

private Object getCellValue(String cellValue, Field field) {

if (StringUtils.isBlank(cellValue)) {

return null;

}

String value = convertCellValue(cellValue, field);

if (String.class.isAssignableFrom(field.getType())) {

return value;

}

if (Integer.class.isAssignableFrom(field.getType())) {

return Integer.parseInt(value);

}

if (Long.class.isAssignableFrom(field.getType())) {

return Long.parseLong(value);

}

return cellValue;

}

private String convertCellValue(String cellValue, Field field) {

final Cell annotation = field.getAnnotation(Cell.class);

if (Objects.nonNull(annotation)) {

final boolean need = annotation.needConvert();

if (need) {

final Class convert = annotation.convert();

return EnumHelper.getCodeByText(convert, cellValue);

}

}

return cellValue;

}

private Field getField(String cellAddress, Map rowBeanFieldMap) {

String columnCode = getColumnCode(cellAddress);

return rowBeanFieldMap.get(columnCode);

}

/**

* 获取单元格的列头代码

*

* @param cellAddress A1,B1,C1...

* @return

*/

private String getColumnCode(String cellAddress) {

return cellAddress.replaceAll("\\d+$", "");

}

}

MyDataFormatter.java

import org.apache.poi.ss.usermodel.DataFormatter;

import org.apache.poi.ss.usermodel.DateUtil;

import java.text.SimpleDateFormat;

/**

* @author ff

* @date 2022/8/30 17:48

*/

public class MyDataFormatter extends DataFormatter {

@Override

public String formatRawCellContents(double value, int formatIndex, String formatString, boolean use1904Windowing) {

if (DateUtil.isADateFormat(formatIndex, formatString)) {

if (DateUtil.isValidExcelDate(value)) {

return new SimpleDateFormat("yyyy-MM-dd").format(value);

}

}

return super.formatRawCellContents(value, formatIndex, formatString);

}

}

测试结果就不放了,效率刚刚的,完美解决OOM~

推荐阅读

评论可见,请评论后查看内容,谢谢!!!
 您阅读本篇文章共花了: