Java 有2个jar包可以操作excel文件,分别是jxl和poi;
jxl这个jar包只能读取excel2003年的文件(文件后缀为.xls),而poi这个jar包excel2003(文件后缀为.xls)和excel2007(文件后缀为.xls)的文件都可以读取。
我是用的是poi这个jar包,对excel进行读取;
下面是上传一个file文件时调用的方法
public static Workbook getWorkbookByMultipartFile(MultipartFile file){
String fileName=file.getOriginalFilename();
if (!fileName.matches("^.+\\.(?i)(xls)$") && !fileName.matches("^.+\\.(?i)(xlsx)$")) {
log.info("文件格式错误,文件名:{}",fileName);
return null;
}
boolean isExcelXlsx2007 = false;
if (fileName.matches("^.+\\.(?i)(xlsx)$")) {
isExcelXlsx2007 = true;
}
InputStream is = null;
try {
is = file.getInputStream();
} catch (IOException e) {
log.error("读取黄页出错,file.getInputStream();失败",e);
return null;
}
Workbook wb = null;
if (isExcelXlsx2007) {
try {
wb = new XSSFWorkbook(is);
} catch (IOException e) {
log.error("读取ExcelXlsx2007出错",e);
}finally {
if (is!=null){
try {
is.close();
} catch (IOException e) {
log.error("关闭文件流出错",e);
}
}
return wb;
}
}
try {
wb = new HSSFWorkbook(is);
} catch (IOException e) {
log.error("读取ExcelXlsx2007出错",e);
}finally {
if (is!=null){
try {
is.close();
} catch (IOException e) {
log.error("关闭文件流出错",e);
}
}
return wb;
}
}
调用这个方法就能获取一个Workbook对象,然后获取sheet对象,对其中的数据进行处理,这个肯定是没有问题的,但是这个方法是把文件转化为inputstream流,然后想通过这个流来获取一个Workbook对象,当文件很大的时候(我操作20多万条数据时,文件大约15M左右),这个步骤就会出现内存溢出的问题。这个问题直接就把后路断死了,只能另外找办法解决。
使用poi的另一个模式,poi Sax事件驱动解析excel
/**
* Excle xxls 批量读取大文件操作类
*
*/
public abstract class XlsxProcessAbstract {
private final Logger logger = LoggerFactory.getLogger(XlsxProcessAbstract.class);
//开始读取行数从第0行开始计算
private int rowIndex = -1;
private final int minColumns = 0;
/**
* Destination for data
*/
public LinkedList processAllSheet(Integer index, Class clazz) throws Exception {
OPCPackage pkg = OPCPackage.open(this.getInputStream());
ReadOnlySharedStringsTable strings = new ReadOnlySharedStringsTable(pkg);
XSSFReader xssfReader = new XSSFReader(pkg);
StylesTable styles = xssfReader.getStylesTable();
SheetToCSV sheetToCSV = new SheetToCSV(clazz);
parserSheetXml(styles, strings, sheetToCSV, xssfReader.getSheet("rId"+index));
return sheetToCSV.getPojoList();
}
/**
* 解析excel 转换成xml
*
* @param styles
* @param strings
* @param sheetHandler
* @param sheetInputStream
* @throws IOException
* @throws SAXException
*/
public void parserSheetXml(StylesTable styles, ReadOnlySharedStringsTable strings, SheetContentsHandler sheetHandler, InputStream sheetInputStream) throws IOException, SAXException {
DataFormatter formatter = new DataFormatter();
InputSource sheetSource = new InputSource(sheetInputStream);
try {
XMLReader sheetParser = SAXHelper.newXMLReader();
ContentHandler handler = new XSSFSheetXMLHandler(styles, null, strings, sheetHandler, formatter, false);
sheetParser.setContentHandler(handler);
sheetParser.parse(sheetSource);
} catch (ParserConfigurationException e) {
throw new RuntimeException("SAX parser appears to be broken - " + e);
}
}
public abstract InputStream getInputStream() throws IOException;
/**
* 读取excel行、列值
*
* @author nevin.zhang
*/
private class SheetToCSV implements SheetContentsHandler {
private boolean firstCellOfRow = false;
private T pojo;
private Class clazz;
private int currentRowNumber = -1;
private int currentColNumber = -1;
private ArrayList keyList = Lists.newArrayList();
private LinkedList pojoList = Lists.newLinkedList();
public LinkedList getPojoList() {
return pojoList;
}
public SheetToCSV(Class clazz) {
this.clazz = clazz;
}
/**
* 处理cell中为空值的情况
* @param number
*/
private void processCellBlankCells(int number) {
for (int i = 0; i < number; i++) {
for (int j = 0; j < minColumns; j++) {
}
}
}
@Override
public void startRow(int rowNum) {
//logger.info(String.valueOf(rowNum));
processCellBlankCells(rowNum - currentRowNumber - 1);
if(rowNum!=0){
try {
pojo = clazz.newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
firstCellOfRow = true;
currentRowNumber = rowNum;
currentColNumber = -1;
}
@Override
public void endRow(int rowNum) {
if(pojo==null){
return;
}
System.out.println(pojo);
if (currentRowNumber!=0){
pojoList.add(pojo);
}
}
@Override
public void cell(String cellReference, String cellValue, XSSFComment comment) {
if (firstCellOfRow) {
firstCellOfRow = false;
} else {
}
if (cellReference == null) {
cellReference = new CellAddress(currentRowNumber, currentColNumber).formatAsString();
}
int thisCol = (new CellReference(cellReference)).getCol();
int missedCols = thisCol - currentColNumber - 1;
for (int i = 0; i < missedCols; i++) {
// excel中为空的值设置为“|@|”
}
currentColNumber = thisCol;
// logger.info("当前行数:{},当前列数:{},当前值cell:{}",currentRowNumber, currentColNumber, cellValue);
if (currentRowNumber ==0){
keyList.add(cellValue);
return;
}
if (pojo == null||StringUtils.isBlank(cellValue)) {
return;
}
try {
PropertyDescriptor pd = new PropertyDescriptor(keyList.get(currentColNumber), clazz);
try {
pd.getWriteMethod().invoke(pojo, cellValue);
pd=new PropertyDescriptor("createBy",clazz);
pd.getWriteMethod().invoke(pojo, RequestHolder.getCurrentUser().getUsername());
pd=new PropertyDescriptor("updateBy",clazz);
pd.getWriteMethod().invoke(pojo, RequestHolder.getCurrentUser().getUsername());
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
} catch (IntrospectionException e) {
e.printStackTrace();
}
}
@Override
public void headerFooter(String text, boolean isHeader, String tagName) {
}
}
}
该方法对excel的数据有规定的要求——excel的第1行全放属性名,下面的2,3.....行都是放置对应的数据,遍历每行都会生成一个对应的pojo实体类对象。
对象千万用LinkedList来存,不能用Arraylist,因为这种方法不能直接取到excel的数据条数,导致不能直接new Arratlist,Arraylist会自动扩展,浪费内存。
最后将LinkedList传到service层,调用dao层的方法进行数据插入,这里又会出问题,因为我是用的时Mybatis,他会自动代理mapper中的方法,把传过来的list会再copy一遍,又会出现内存溢出的问题,只能将这个数组进行分批插入的操作,最终才完成。