java工具类之excel大量数据读取工具类

工具类:

import java.io.FileInputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.poi.hssf.eventusermodel.EventWorkbookBuilder;
import org.apache.poi.hssf.eventusermodel.FormatTrackingHSSFListener;
import org.apache.poi.hssf.eventusermodel.HSSFEventFactory;
import org.apache.poi.hssf.eventusermodel.HSSFListener;
import org.apache.poi.hssf.eventusermodel.HSSFRequest;
import org.apache.poi.hssf.eventusermodel.MissingRecordAwareHSSFListener;
import org.apache.poi.hssf.eventusermodel.dummyrecord.LastCellOfRowDummyRecord;
import org.apache.poi.hssf.eventusermodel.dummyrecord.MissingCellDummyRecord;
import org.apache.poi.hssf.model.HSSFFormulaParser;
import org.apache.poi.hssf.record.BOFRecord;
import org.apache.poi.hssf.record.BlankRecord;
import org.apache.poi.hssf.record.BoolErrRecord;
import org.apache.poi.hssf.record.BoundSheetRecord;
import org.apache.poi.hssf.record.FormulaRecord;
import org.apache.poi.hssf.record.LabelRecord;
import org.apache.poi.hssf.record.LabelSSTRecord;
import org.apache.poi.hssf.record.NumberRecord;
import org.apache.poi.hssf.record.Record;
import org.apache.poi.hssf.record.SSTRecord;
import org.apache.poi.hssf.record.StringRecord;
import org.apache.poi.hssf.usermodel.HSSFDataFormatter;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.openxml4j.opc.OPCPackage;
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
import org.apache.poi.ss.usermodel.BuiltinFormats;
import org.apache.poi.ss.usermodel.DataFormatter;
import org.apache.poi.xssf.eventusermodel.XSSFReader;
import org.apache.poi.xssf.model.SharedStringsTable;
import org.apache.poi.xssf.model.StylesTable;
import org.apache.poi.xssf.usermodel.XSSFCellStyle;
import org.apache.poi.xssf.usermodel.XSSFRichTextString;
import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.DefaultHandler;
import org.xml.sax.helpers.XMLReaderFactory;

/**
 * excel大量数据读取工具类
 * 
 * @author zql by 2019-10-31
 * @api http://poi.apache.org/components/spreadsheet/how-to.html
 */
public class ExcelBigDataReadUtil {

	/**
	 * 读取excel文件
	 * 
	 * @param filePath 文件路径
	 * @param sheetNum 第几个sheetNum工作薄,从1开始,为null的时候表示全部读取
	 * @param processRows 处理行数据的接口
	 * @throws Exception
	 */
	public void readExcel(String filePath,Integer sheetNum,ProcessRows processRows) throws Exception {
		if (filePath.endsWith("xls")) {
			XlsReader xlsR = new XlsReader();
			xlsR.process(filePath, sheetNum, processRows);
		} else if (filePath.endsWith("xlsx")) {
			XlsxReader xlsxR = new XlsxReader();
			xlsxR.process(filePath, sheetNum, processRows);
		} else {
			throw new Exception("Only excel files with XLS or XLSX suffixes are allowed to be read!");
		}
	}
	
	/**
	 * 定义一个处理行数据的接口
	 */
	public interface ProcessRows {
		
		/**
		 * @param sheetName 工作薄名称
		 * @param sheetIndex 工作薄索引
		 * @param curRow 当前行,不包装第一行,第一默认为标题
		 * @param cellMap 当前行的单元格Map集合,key为单元格所对应的标题,value为单元格内容
		 */
		void setRows(String sheetName, int sheetIndex, int curRow, Map<String,Object> cellMap);
	}
	
	/**
	 *  用于excel2003版本的读取
	 */
	public class XlsReader implements HSSFListener {
		
		private int minColums = -1;
		
	    private POIFSFileSystem fs;
	    /**
	     * 处理行数据的接口
	     */
	    private ProcessRows processRows;
	    /**
	     * 上一行row的序号
	     */
	    private int lastRowNumber;
	    /**
	     * 上一单元格的序号
	     */
	    private int lastColumnNumber;
	    /**
	     * 是否输出formula,还是它对应的值
	     */
	    private boolean outputFormulaValues = true;
	    /**
	     * 用于转换formulas
	     */
	    private EventWorkbookBuilder.SheetRecordCollectingListener workbookBuildingListener;
	    /**
	     * excel2003工作簿
	     */
	    private HSSFWorkbook stubWorkbook;

	    private SSTRecord sstRecord;

	    private FormatTrackingHSSFListener formatListener;

	    private final HSSFDataFormatter formatter = new HSSFDataFormatter();
	    /**
	     * 表索引
	     */
	    private int sheetIndex = 0;

	    private BoundSheetRecord[] orderedBSRs;

		private List<BoundSheetRecord> boundSheetRecords = new ArrayList<BoundSheetRecord>();

	    private int nextRow;

	    private int nextColumn;

	    private boolean outputNextStringRecord;
	    /**
	     * 当前行
	     */
	    private int curRow = 0;
	    /**
	     * 存储一行记录所有单元格的Map容器
	     */
	    private Map<String,Object> cellMap = new HashMap<String,Object>();
	    /**
	     * 存储标题行所有单元格的Map容器
	     */
	    private Map<Integer,String> titles = new HashMap<Integer,String>();
	    /**
	     * 判断整行是否为空行的标记
	     */
	    private boolean flag = false;

	    private String sheetName;
	    /**
	     * 指定的sheet,默认值为-1,当为-1时,表示传入的值为null,将读取所有的sheet
	     */
	    private int sheetNum = -1;
	    
	    /**
	     * 处理数据
	     * 
	     * @param filePath 文件路径
	     * @param sheetNum 第几个sheetNum工作薄,从1开始,为null的时候表示全部读取,小于或等于0时默认为第1个工作薄
	     * @param processRows 处理行数据的接口
	     * @throws Exception
	     */
	    public void process(String filePath, Integer sheetNum, ProcessRows processRows) throws Exception {
			this.processRows = processRows;
			if (sheetNum != null && sheetNum <= 0) {
				sheetNum = 1;
			}
			if (sheetNum != null) {
				this.sheetNum = sheetNum;
			}
			this.fs = new POIFSFileSystem(new FileInputStream(filePath));
			MissingRecordAwareHSSFListener listener = new MissingRecordAwareHSSFListener(this);
			formatListener = new FormatTrackingHSSFListener(listener);
			HSSFEventFactory factory = new HSSFEventFactory();
			HSSFRequest request = new HSSFRequest();
			if (outputFormulaValues) {
				request.addListenerForAllRecords(formatListener);
			} else {
				workbookBuildingListener = new EventWorkbookBuilder.SheetRecordCollectingListener(formatListener);
				request.addListenerForAllRecords(workbookBuildingListener);
			}
			factory.processWorkbookEvents(request, fs);
	    }
	    
		/**
	     * HSSFListener 监听方法,处理Record(处理每个单元格)
	     * 
	     * @param record
	     */
		@Override
		public void processRecord(Record record) {
			int thisRow = -1;
			int thisColumn = -1;
			String thisStr = null;
			String value = null;
			switch (record.getSid()) {
			case BoundSheetRecord.sid:
				boundSheetRecords.add((BoundSheetRecord) record);
				break;
			// 开始处理每个sheet
			case BOFRecord.sid:
				BOFRecord br = (BOFRecord) record;
				if (br.getType() == BOFRecord.TYPE_WORKSHEET) {
					// 如果有需要,则建立子工作簿
					if (workbookBuildingListener != null && stubWorkbook == null) {
						stubWorkbook = workbookBuildingListener.getStubHSSFWorkbook();
					}
					if (orderedBSRs == null) {
						orderedBSRs = BoundSheetRecord.orderByBofPosition(boundSheetRecords);
					}
					sheetName = orderedBSRs[sheetIndex].getSheetname();
					sheetIndex++;
				}
				break;
			case SSTRecord.sid:
				sstRecord = (SSTRecord) record;
				break;
			// 单元格为空白
			case BlankRecord.sid:
				BlankRecord brec = (BlankRecord) record;
				thisRow = brec.getRow();
				thisColumn = brec.getColumn();
				thisStr = "";
				if (curRow == 0) {
					titles.put(thisColumn, thisStr);
				} else {
					cellMap.put(titles.get(thisColumn), thisStr);
				}
				break;
			// 单元格为布尔类型
			case BoolErrRecord.sid:
				BoolErrRecord berec = (BoolErrRecord) record;
				thisRow = berec.getRow();
				thisColumn = berec.getColumn();
				thisStr = berec.getBooleanValue() + "";
				if (curRow == 0) {
					titles.put(thisColumn, thisStr);
				} else {
					cellMap.put(titles.get(thisColumn), thisStr);
				}
				// 如果里面某个单元格含有值,则标识该行不为空行
				checkRowIsNull(thisStr);
				break;
			// 单元格为公式类型
			case FormulaRecord.sid:
				FormulaRecord frec = (FormulaRecord) record;
				thisRow = frec.getRow();
				thisColumn = frec.getColumn();
				if (outputFormulaValues) {
					if (Double.isNaN(frec.getValue())) {
						outputNextStringRecord = true;
						nextRow = frec.getRow();
						nextColumn = frec.getColumn();
					} else {
						thisStr = '"' + HSSFFormulaParser.toFormulaString(stubWorkbook, frec.getParsedExpression())
								+ '"';
					}
				} else {
					thisStr = '"' + HSSFFormulaParser.toFormulaString(stubWorkbook, frec.getParsedExpression()) + '"';
				}
				if (curRow == 0) {
					titles.put(thisColumn, thisStr);
				} else {
					cellMap.put(titles.get(thisColumn), thisStr);
				}
				// 如果里面某个单元格含有值,则标识该行不为空行
				checkRowIsNull(thisStr);
				break;
			// 单元格中公式的字符串
			case StringRecord.sid:
				if (outputNextStringRecord) {
					StringRecord srec = (StringRecord) record;
					thisStr = srec.getString();
					thisRow = nextRow;
					thisColumn = nextColumn;
					outputNextStringRecord = false;
				}
				break;
			case LabelRecord.sid:
				LabelRecord lrec = (LabelRecord) record;
				curRow = thisRow = lrec.getRow();
				thisColumn = lrec.getColumn();
				value = lrec.getValue().trim();
				if (curRow == 0) {
					value = value.equals("") ? "空标题" + thisColumn : value;
					titles.put(thisColumn, value);
				} else {
					value = value.equals("") ? "" : value;
					cellMap.put(titles.get(thisColumn), value);
				}
				// 如果里面某个单元格含有值,则标识该行不为空行
				checkRowIsNull(value);
				break;
			// 单元格为字符串类型
			case LabelSSTRecord.sid:
				LabelSSTRecord lsrec = (LabelSSTRecord) record;
				curRow = thisRow = lsrec.getRow();
				thisColumn = lsrec.getColumn();
				if (sstRecord == null) {
					if (curRow == 0) {
						titles.put(thisColumn, "空标题" + thisColumn);
					} else {
						cellMap.put(titles.get(thisColumn), "");
					}
				} else {
					value = sstRecord.getString(lsrec.getSSTIndex()).toString().trim();
					if (curRow == 0) {
						value = value.equals("") ? "空标题" + thisColumn : value;
						titles.put(thisColumn, value);
					} else {
						value = value.equals("") ? "" : value;
						cellMap.put(titles.get(thisColumn), value);
					}
					// 如果里面某个单元格含有值,则标识该行不为空行
					checkRowIsNull(value);
				}
				break;
			// 单元格为数字类型
			case NumberRecord.sid:
				NumberRecord numrec = (NumberRecord) record;
				curRow = thisRow = numrec.getRow();
				thisColumn = numrec.getColumn();
				// 第一种方式,这个被写死,采用的m/d/yy h:mm格式,不符合要求
				/// value = formatListener.formatNumberDateCell(numrec).trim();
				// 第二种方式,参照formatNumberDateCell里面的实现方法编写
				Double valueDouble = ((NumberRecord) numrec).getValue();
				String formatString = formatListener.getFormatString(numrec);
				if (formatString.contains("m/d/yy")) {
					formatString = "yyyy-MM-dd hh:mm:ss";
				}
				int formatIndex = formatListener.getFormatIndex(numrec);
				value = formatter.formatRawCellContents(valueDouble, formatIndex, formatString).trim();

				if (curRow == 0) {
					value = value.equals("") ? "空标题" + thisColumn : value;
					titles.put(thisColumn, value);
				} else {
					value = value.equals("") ? "" : value;
					// 向容器加入列值
					cellMap.put(titles.get(thisColumn), value);
				}
				// 如果里面某个单元格含有值,则标识该行不为空行
				checkRowIsNull(value);
				break;
			default:
				break;
			}
			// 遇到新行的操作
			if (thisRow != -1 && thisRow != lastRowNumber) {
				lastColumnNumber = -1;
			}
			// 空值的操作
			if (record instanceof MissingCellDummyRecord) {
				MissingCellDummyRecord mc = (MissingCellDummyRecord) record;
				curRow = thisRow = mc.getRow();
				thisColumn = mc.getColumn();
				if (curRow == 0) {
					titles.put(thisColumn, "空标题" + thisColumn);
				} else {
					cellMap.put(titles.get(thisColumn), "");
				}
			}
			// 更新行和列的值
			if (thisRow > -1) {
				lastRowNumber = thisRow;
			}
			if (thisColumn > -1) {
				lastColumnNumber = thisColumn;
			}
			// 行结束时的操作
			if (record instanceof LastCellOfRowDummyRecord) {
				if (minColums > 0) {
					// 列值重新置空
					if (lastColumnNumber == -1) {
						lastColumnNumber = 0;
					}
				}
				lastColumnNumber = -1;
				// 该行不为空行且该行不是第一行,并且sheet索引等于指定sheet或指定sheet为默认值-1(即sheetNum==-1表示读取全部sheet),发送(第一行为列名,不需要)
				if (flag && curRow != 0 && (sheetIndex == sheetNum || sheetNum == -1)) {
					processRows.setRows(sheetName, sheetIndex, curRow + 1, cellMap);
				}
				flag = false;
			}
		}
		
		/**
	     * 如果里面某个单元格含有值,则标识该行不为空行
	     * 
	     * @param value
	     */
	    public void checkRowIsNull(String value) {
	        if (value != null && !"".equals(value)) {
	            flag = true;
	        }
	    }
	}
	
	/**
	 * 单元格中的数据可能的类型
	 */
	enum CellType {
		/**空值*/
		NULL,
		/**日期类型*/
		DATE,
		/**INLINESTR*/
		INLINESTR,
		/**数字类型*/
	    NUMERIC,
	    /**字符串类型 */
	    STRING,
	    /**公式类型*/
	    FORMULA,
	    /**布尔值类型*/
	    BOOLEAN,
	    /**错误类型*/
	    ERROR;
	}
	
	/**
	 * 用于excel2007版本的读取
	 */
	public class XlsxReader extends DefaultHandler {
		
		/**
	     * 共享字符串表
	     */
	    private SharedStringsTable sst;
	    /**
	     * 标题行Map集合
	     */
	    private Map<Integer,String> titles = new HashMap<Integer,String>();
	    /**
	     * 上一次的索引值
	     */
	    private String lastIndex;
	    /**
	     * 工作表索引
	     */
	    private int sheetIndex = 0;
	    /**
	     * sheet名称
	     */
	    private String sheetName = "";
	    /**
	     * 一行内cell集合
	     */
	    private Map<String,Object> cellMap = new HashMap<String,Object>();
	    /**
	     * 判断整行是否为空行的标记
	     */
	    private boolean flag = false;
	    /**
	     * 当前行
	     */
	    private int curRow = 1;
	    /**
	     * 当前列
	     */
	    private int curCol = 0;
	    /**
	     * T元素标识
	     */
	    private boolean isTElement;
	    /**
	     * 判断上一单元格是否为文本空单元格
	     */
	    private boolean startElementFlag = true;
	    private boolean endElementFlag = false;
	    private boolean charactersFlag = false;
	    /**
	     * 单元格数据类型,默认为字符串类型
	     */
	    private CellType nextCellType = CellType.STRING;

	    private final DataFormatter formatter = new DataFormatter();
	    /**
	     * 单元格日期格式的索引
	     */
	    private short formatIndex;
	    /**
	     * 日期格式字符串
	     */
	    private String formatString;
	    /**
	     * 定义前一个元素和当前元素的位置,用来计算其中空的单元格数量,如A6和A8等
	     */
	    private String preRef = null, ref = null;
	    /**
	     * 定义该文档一行最大的单元格数,用来补全一行最后可能缺失的单元格
	     */
	    private String maxRef = null;
	    /**
	     * 单元格样式表
	     */
	    private StylesTable stylesTable;
	    /**
	     * 处理行数据的接口
	     */
	    private ProcessRows processRows;
	    
	    /**
	     * 处理数据
	     * 
	     * @param filePath 文件路径
	     * @param sheetNum 第几个sheetNum工作薄,从1开始,为null的时候表示全部读取,小于或等于0时默认为第1个工作薄
	     * @param processRows 处理行数据的接口
	     * @throws Exception
	     */
	    public void process(String filePath, Integer sheetNum, ProcessRows processRows) throws Exception {
			if (sheetNum != null && sheetNum <= 0) {
				sheetNum = 1;
			}
			this.processRows = processRows;
			OPCPackage pkg = OPCPackage.open(filePath);
			XSSFReader xssfReader = new XSSFReader(pkg);
			stylesTable = xssfReader.getStylesTable();
			SharedStringsTable sst = xssfReader.getSharedStringsTable();
			// xml读取类
			XMLReader parser = XMLReaderFactory.createXMLReader("org.apache.xerces.parsers.SAXParser");
			this.sst = sst;
			parser.setContentHandler(this);
			XSSFReader.SheetIterator sheets = (XSSFReader.SheetIterator) xssfReader.getSheetsData();
			// 遍历sheet
			while (sheets.hasNext()) {
				// 标记初始行为第一行
				curRow = 1;
				sheetIndex++;
				// sheets.next()和sheets.getSheetName()不能换位置,否则sheetName报错
				InputStream sheet = sheets.next();
				if (sheetNum == null || sheetNum == sheetIndex) {
					sheetName = sheets.getSheetName();
					InputSource sheetSource = new InputSource(sheet);
					// 解析excel的每条记录,在这个过程中startElement()、characters()、endElement()这三个函数会依次执行
					parser.parse(sheetSource);
					sheet.close();
				}
			}
	    }
	    
	    /**
	     * 重写DefaultHandler的startElement方法,该方法第一个执行
	     * 
	     * @param uri 名称空间URI,如果元素没有名称空间URI,或者没有执行名称空间处理,则为空字符串。
	     * @param localName 本地名称(没有前缀),如果没有执行名称空间处理,则为空字符串。
	     * @param qName 限定名(带前缀),如果没有限定名,则使用空字符串。
	     * @param attributes 附加到元素的属性。如果没有属性,则为空属性对象。
	     * @throws SAXException
	     */
	    @Override
	    public void startElement(String uri, String localName,
                String qName, Attributes attributes) throws SAXException {
			// c => 单元格
			if ("c".equals(qName)) {
				// 前一个单元格的位置
				if (preRef == null) {
					preRef = attributes.getValue("r");
				} else {
					// 中部文本空单元格标识 ‘endElementFlag’
					// 判断前一次是否为文本空字符串,true则表明不是文本空字符串,false表明是文本空字符串跳过把空字符串的位置赋予preRef
					if (endElementFlag) {
						preRef = ref;
					}
				}
				// 当前单元格的位置
				ref = attributes.getValue("r");
				// 首部文本空单元格标识 ‘startElementFlag’
				// 判断前一次,即首部是否为文本空字符串,true则表明不是文本空字符串,false表明是文本空字符串,且已知当前格,即第二格带“B”标志,则ref赋予preRef
				// 上一个单元格为文本空单元格,执行下面的,使ref=preRef;flag为true表明该单元格之前有数据值,即该单元格不是首部空单元格,则跳过
				if (!startElementFlag && !flag) {
					// 这里只有上一个单元格为文本空单元格,且之前的几个单元格都没有值才会执行
					preRef = ref;
				}
				// 设定单元格类型
				this.setNextCellType(attributes);
				endElementFlag = false;
				charactersFlag = false;
				startElementFlag = false;
			}
			// 当元素为t时
			if ("t".equals(qName)) {
				isTElement = true;
			} else {
				isTElement = false;
			}
			// 置空
			lastIndex = "";
	    }
	    
	    /**
	     * 重写DefaultHandler的characters方法,该方法第二个执行
	     * 得到单元格对应的索引值或是内容值
	     * 如果单元格类型是字符串、INLINESTR、数字、日期,lastIndex则是索引值
	     * 如果单元格类型是布尔值、错误、公式,lastIndex则是内容值
	     * 
	     * @param ch 字符数组
	     * @param start 字符数组中的起始位置。
	     * @param length 要从字符数组中使用的字符数。
	     * @throws SAXException
	     */
	    @Override
	    public void characters(char[] ch, int start, int length) throws SAXException {
	        startElementFlag = true;
	        charactersFlag = true;
	        lastIndex += new String(ch, start, length);
	    }
	    
	    /**
	     * 重写DefaultHandler的endElement方法,该方法第三个执行
	     * 
	     * @param uri 名称空间URI,如果元素没有名称空间URI,或者没有执行名称空间处理,则为空字符串。
	     * @param localName 本地名称(没有前缀),如果没有执行名称空间处理,则为空字符串。
	     * @param qName 限定名(带前缀),如果没有限定名,则使用空字符串。
	     * @throws SAXException
	     */
	    @Override
	    public void endElement(String uri, String localName, String qName) throws SAXException {
			// t元素也包含字符串
	    	// 这个程序没经过
			if (isTElement) {
				// 将单元格内容加入cellMap中,在这之前先去掉字符串前后的空白符
				String value = lastIndex.trim();
				cellMap.put(titles.get(curCol), value);
				endElementFlag = true;
				curCol++;
				isTElement = false;
				// 如果里面某个单元格含有值,则标识该行不为空行
				if (value != null && !"".equals(value)) {
					flag = true;
				}
			} else if ("v".equals(qName)) {
				// v => 单元格的值,如果单元格是字符串,则v标签的值为该字符串在SST中的索引
				// 根据索引值获取对应的单元格值
				String value = this.getDataValue(lastIndex.trim(), "");
				// 默认第一行为标题,把标题装入数组
				if (curRow == 1) {
					// 补全标题行单元格之间的空单元格
					if (!ref.equals(preRef)) {
						int len = countNullCell(ref, preRef);
						for (int i = 0; i < len; i++) {
							titles.put(curCol, "空标题" + curCol);
							curCol++;
						}
						// ref等于preRef,且以B或者C...开头,表明首部为空格
					} else if (ref.equals(preRef)) { 
						int len = countNullCell(ref, "A");
						for (int i = 0; i < len; i++) {
							titles.put(curCol, "空标题" + curCol);
							curCol++;
						}
					}
					titles.put(curCol, value);
				} else {
					// 补全单元格之间的空单元格
					if (!ref.equals(preRef)) {
						int len = countNullCell(ref, preRef);
						for (int i = 0; i < len; i++) {
							cellMap.put(titles.get(curCol), "");
							curCol++;
						}
						// ref等于preRef,且以B或者C...开头,表明首部为空格
					} else if (ref.equals(preRef)) {
						cellMap.put(titles.get(curCol), "");
						int len = countNullCell(ref, "A");
						for (int i = 0; i < len; i++) {
							cellMap.put(titles.get(curCol), "");
							curCol++;
						}
					}
					cellMap.put(titles.get(curCol), value);
				}
				curCol++;
				endElementFlag = true;
				// 如果里面某个单元格含有值,则标识该行不为空行
				if (value != null && !"".equals(value)) {
					flag = true;
				}
			} else {
				// 如果标签名称为row,这说明已到行尾,调用setRows()方法
				if ("row".equals(qName)) {
					// 默认第一行为表头,以该行单元格数目为最大数目
					if (curRow == 1) {
						maxRef = ref;
					}
					// 补全一行尾部可能缺失的单元格
					if (maxRef != null) {
						int len = -1;
						// 前一单元格,true则不是文本空字符串,false则是文本空字符串
						if (charactersFlag) {
							len = countNullCell(maxRef, ref);
						} else {
							len = countNullCell(maxRef, preRef);
						}
						for (int i = 0; i <= len; i++) {
							cellMap.put(titles.get(curCol), "");
							curCol++;
						}
					}
					// 该行不为空行且该行不是第一行,则设置(第一行为列名,不需要)
					if (flag && curRow != 1) {
						processRows.setRows(sheetName, sheetIndex, curRow, cellMap);
					}
					curRow++;
					curCol = 0;
					preRef = null;
					ref = null;
					flag = false;
				}
			}
	    }
	    
	    /**
	     * 处理数据类型
	     * 
	     * @param attributes
	     */
	    public void setNextCellType(Attributes attributes) {
			// cellType为空,则表示该单元格类型为数字
			nextCellType = CellType.NUMERIC;
			formatIndex = -1;
			formatString = null;
			// 单元格类型
			String cellType = attributes.getValue("t");
			String cellStyleStr = attributes.getValue("s");
			// 处理布尔值
			if ("b".equals(cellType)) {
				nextCellType = CellType.BOOLEAN;
				// 处理错误
			} else if ("e".equals(cellType)) {
				nextCellType = CellType.ERROR;
			} else if ("inlineStr".equals(cellType)) {
				nextCellType = CellType.INLINESTR;
				// 处理字符串
			} else if ("s".equals(cellType)) {
				nextCellType = CellType.STRING;
				// 处理公式
			} else if ("str".equals(cellType)) {
				nextCellType = CellType.FORMULA;
			}
			// 处理日期
			if (cellStyleStr != null) {
				int styleIndex = Integer.parseInt(cellStyleStr);
				XSSFCellStyle style = stylesTable.getStyleAt(styleIndex);
				formatIndex = style.getDataFormat();
				formatString = style.getDataFormatString();
				if (formatString.contains("m/d/yyyy") || formatString.contains("yyyy/mm/dd")
						|| formatString.contains("yyyy/m/d")) {
					nextCellType = CellType.DATE;
					formatString = "yyyy-MM-dd hh:mm:ss";
				}
				if (formatString == null) {
					nextCellType = CellType.NULL;
					formatString = BuiltinFormats.getBuiltinFormat(formatIndex);
				}
			}
	    }
	    
	    /**
	     * 对解析出来的数据进行类型处理
	     * @param value   单元格的值,
	     *                value代表解析:BOOL的为0或1, ERROR的为内容值,FORMULA的为内容值,INLINESTR的为索引值需转换为内容值,
	     *                SSTINDEX的为索引值需转换为内容值, NUMBER为内容值,DATE为内容值
	     * @param thisStr 一个空字符串
	     * @return
	     */
	    public String getDataValue(String value, String thisStr) {
	    	// 这几个的顺序不能随便交换,交换了很可能会导致数据错误
			switch (nextCellType) {
			// 布尔值
			case BOOLEAN:
				char first = value.charAt(0);
				thisStr = first == '0' ? "FALSE" : "TRUE";
				break;
			// 错误
			case ERROR:
				thisStr = "\"ERROR:" + value.toString() + '"';
				break;
			// 公式
			case FORMULA:
				thisStr = '"' + value.toString() + '"';
				break;
			case INLINESTR:
				XSSFRichTextString rtsi = new XSSFRichTextString(value.toString());
				thisStr = rtsi.toString();
				rtsi = null;
				break;
			// 字符串
			case STRING:
				String sstIndex = value.toString();
				try {
					int idx = Integer.parseInt(sstIndex);
					// 根据idx索引值获取内容值,旧版本XSSFRichTextString rtss = new XSSFRichTextString(sst.getEntryAt(idx));
					XSSFRichTextString rtss = (XSSFRichTextString) sst.getItemAt(idx);
					thisStr = rtss.toString();
					// 有些字符串是文本格式的,但内容却是日期
					rtss = null;
				} catch (NumberFormatException ex) {
					thisStr = value.toString();
				}
				break;
			// 数字
			case NUMERIC:
				if (formatString != null) {
					thisStr = formatter.formatRawCellContents(Double.parseDouble(value), formatIndex, formatString).trim();
				} else {
					thisStr = value;
				}
				thisStr = thisStr.replace("_", "").trim();
				break;
			// 日期
			case DATE:
				thisStr = formatter.formatRawCellContents(Double.parseDouble(value), formatIndex, formatString);
				// 对日期字符串作特殊处理,去掉T
				thisStr = thisStr.replace("T", " ");
				break;
			default:
				thisStr = " ";
				break;
			}
			return thisStr;
	    }
	    
	    /**
	     * 得到两个单元格之间的空单元格数量
	     * 
	     * @param ref 当前单元格位置
	     * @param preRef 前一个单元格位置
	     * @return
	     */
	    public int countNullCell(String ref, String preRef) {
			// excel2007最大行数是1048576,最大列数是16384,最后一列列名是XFD
			String xfd = ref.replaceAll("\\d+", "");
			String xfd_1 = preRef.replaceAll("\\d+", "");

			xfd = fillChar(xfd, 3, '@', true);
			xfd_1 = fillChar(xfd_1, 3, '@', true);

			char[] letter = xfd.toCharArray();
			char[] letter_1 = xfd_1.toCharArray();
			int res = (letter[0] - letter_1[0]) * 26 * 26 + (letter[1] - letter_1[1]) * 26 + (letter[2] - letter_1[2]);
			return res - 1;
	    }
	    
	    public String fillChar(String str, int len, char let, boolean isPre) {
			int len_1 = str.length();
			if (len_1 < len) {
				if (isPre) {
					for (int i = 0; i < (len - len_1); i++) {
						str = let + str;
					}
				} else {
					for (int i = 0; i < (len - len_1); i++) {
						str = str + let;
					}
				}
			}
			return str;
	    }
	}
}

测试类:

import java.util.Map;

/**
 * excel大量数据读取工具测试类
 * 
 * @author zql
 */
public class ExcelBigDataReadUtilTest {

	public static void main(String[] args) throws Exception {
		ExcelBigDataReadUtil e = new ExcelBigDataReadUtil();
		String filePath = "D:\\excel\\测试数据_65536条.xls";
		e.readExcel(filePath, 1, new ProcessRows() {
			@Override
			public void setRows(String sheetName, int sheetIndex, int curRow, Map<String, Object> cellMap) {
				StringBuffer sb = new StringBuffer();
				sb.append("sheet" + sheetIndex);
				sb.append("::" + sheetName);
				sb.append("--");
				sb.append("row_" + curRow);
				sb.append("{");
				for (Map.Entry<String, Object> cell : cellMap.entrySet()) {
					sb.append(cell.getKey());
					sb.append(":");
					sb.append(cell.getValue());
					sb.append("|");
				}
				String line = sb.toString();
				if (line.endsWith("|")) {
					line = line.substring(0, line.lastIndexOf("|"));
				} // 去除最后一个分隔符
				line += "}";
				System.out.println(line);
			}
		});
		String filePath2 = "D:\\excel\\测试数据_100万条.xlsx";
		e.readExcel(filePath2, 2, new ProcessRows() {
			@Override
			public void setRows(String sheetName, int sheetIndex, int curRow, Map<String, Object> cellMap) {
				StringBuffer sb = new StringBuffer();
				sb.append("sheet" + sheetIndex);
				sb.append("::" + sheetName);
				sb.append("--");
				sb.append("row_" + curRow);
				sb.append("{");
				for (Map.Entry<String, Object> cell : cellMap.entrySet()) {
					sb.append(cell.getKey());
					sb.append(":");
					sb.append(cell.getValue());
					sb.append("|");
				}
				String line = sb.toString();
				if (line.endsWith("|")) {
					line = line.substring(0, line.lastIndexOf("|"));
				} // 去除最后一个分隔符
				line += "}";
				System.out.println(line);
			}
		});
	}
}

普通项目需要引入的包
poi-4.0.1.jar
poi-ooxml-4.0.1.jar
poi-ooxml-schemas-4.0.1.jar
commons-codec-1.11.jar
commons-collections4-4.3.jar
commons-math3-3.6.1.jar
xmlbeans-3.0.2.jar
commons-compress-1.18.jar
curvesapi-1.06.jar
xerceslmpl-2.10.0.jar
xml-apis-1.4.01.jar

maven项目依赖

<!-- poi -->
<dependency>
  <groupId>org.apache.poi</groupId>
  <artifactId>poi</artifactId>
  <version>4.0.1</version>
</dependency>
<dependency>
  <groupId>org.apache.poi</groupId>
  <artifactId>poi-ooxml</artifactId>
  <version>4.0.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/xerces/xercesImpl -->
<dependency>
  <groupId>xerces</groupId>
  <artifactId>xercesImpl</artifactId>
  <version>2.10.0</version>
</dependency>

参考链接:https://www.cnblogs.com/swordfall/p/8298386.html

发布了55 篇原创文章 · 获赞 25 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/mr_zql/article/details/103037633