一、适用场景
使用poi读取excel文件有两种方式,一种是用户模式,使用封装好的api操作excel文件。这种模式读取excel文件中的数据会一次性将文件中的内容读取到内存中,虽使用方便,但是当文件数据够大时,会出现内存溢出。这种情况下可以用poi的事件驱动,基于sax的读取方式读取excel文件。
二、poi事件驱动
poi的时间驱动,是区别于用户模式的另一种解析方式,原理是逐行读取excel文件(仅限于excel2007以上版本),在开始读取一行时初始化对象,并在读取每个单元格时给对象赋值,在一行读取结束后将对象加入到一个集合中,以此来实现在大数据量的情况下顺利完成数据读取。
三、代码
1.核心的解析处理器
import com.bigsea.entity.Employee;
import org.apache.poi.xssf.eventusermodel.XSSFSheetXMLHandler;
import org.apache.poi.xssf.usermodel.XSSFComment;
import java.util.ArrayList;
import java.util.List;
/**
* 自定义sheet基于Sax的解析处理器
*/
public class SheetHandler implements XSSFSheetXMLHandler.SheetContentsHandler {
// 封装实体对象
private Employee employee;
// 集合对象
private List<Employee> employeeList;
/**
* 当开始解析某一行的时候触发
* @param i 行号
*/
@Override
public void startRow(int i) {
if (i > 0) {
employee = new Employee();
}
}
/**
* 当结束解析某一行的时候触发
* @param i 行号
*/
@Override
public void endRow(int i) {
if (employeeList == null) {
employeeList = new ArrayList<>();
}
employeeList.add(employee);
// 当list集合大小为1000时,执行插入,并初始化集合对象(此处为伪操作)
if (employeeList.size() == 1000) {
System.out.println(employeeList.size());
employeeList = new ArrayList<>();
}
}
/**
* 对行中的每一个单元格进行处理
* @param cellName 单元格名称
* @param value 数据
* @param xssfComment 批注
*/
@Override
public void cell(String cellName, String value, XSSFComment xssfComment) {
if (employee != null) {
String prefix = cellName.substring(0, 1);
switch (prefix) {
case "A":
employee.setCode(Integer.valueOf(value));
break;
case "B":
employee.setName(value);
break;
case "C":
employee.setSex(value);
break;
case "D":
employee.setDept(value);
break;
case "E":
employee.setPosition(value);
break;
case "F":
employee.setLeaderName(value);
break;
case "G":
employee.setSalary(Double.valueOf(value));
break;
case "H":
employee.setInDateStr(value);
break;
case "I":
employee.setHolidayCount(Integer.valueOf(value));
break;
}
}
}
}
2.在业务层调用sax解析处理器
import com.bigsea.service.ImportExcelService;
import org.apache.poi.openxml4j.opc.OPCPackage;
import org.apache.poi.openxml4j.opc.PackageAccess;
import org.apache.poi.xssf.eventusermodel.XSSFReader;
import org.apache.poi.xssf.eventusermodel.XSSFSheetXMLHandler;
import org.apache.poi.xssf.model.SharedStringsTable;
import org.apache.poi.xssf.model.StylesTable;
import org.springframework.stereotype.Service;
import org.xml.sax.InputSource;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.XMLReaderFactory;
import java.io.InputStream;
/**
* 导入excel文件业务层
* excel读取有两种方式
* 1.用户模式:使用系列封装好的api操作excel
* 2.事件驱动:基于sax的读取方式读取excel的xml文件
*/
@Service
public class ImportServiceImpl implements ImportExcelService {
/**
* 读取大数据量excel
* @parm path 文件路径
*/
@Override
public void readBigDataExcel(String path) throws Exception {
// 1.根据excel报表获取OPCpackage
OPCPackage opcPackage = OPCPackage.open(path, PackageAccess.READ);
// 2.创建XSSFReader
XSSFReader xssfReader = new XSSFReader(opcPackage);
// 3.获取SharedStringTable对象
SharedStringsTable sharedStringsTable = xssfReader.getSharedStringsTable();
// 4.获取styleTable对象
StylesTable stylesTable = xssfReader.getStylesTable();
// 5.创建sax xmlReader对象
XMLReader xmlReader = XMLReaderFactory.createXMLReader();
// 6.注册事件驱动处理器
XSSFSheetXMLHandler xssfSheetXMLHandler = new XSSFSheetXMLHandler(stylesTable, sharedStringsTable, new SheetHandler(), false);
xmlReader.setContentHandler(xssfSheetXMLHandler);
// 7.逐行读取
XSSFReader.SheetIterator sheetIterator = (XSSFReader.SheetIterator) xssfReader.getSheetsData();
while (sheetIterator.hasNext()) {
InputStream in = sheetIterator.next();
InputSource is = new InputSource(in);
xmlReader.parse(is);
}
}
}