1. 程式人生 > >Java用POI實現讀取大資料量Excel

Java用POI實現讀取大資料量Excel

java程式碼使用poi的API解決在讀取大資料量的Excel資料時候記憶體溢位的問題:首先我需要宣告下面的工具類是在老袁部落格(https://laoyuan.me/posts/java-read-big-excel-with-poi.html)基礎上做了稍微的改造,我將老袁的的工具類需要2個引數改成只需要一個引數就可以完成呼叫,當然你可以根據你自己的情況使用。
下面是一個工具類,複製到自己的專案中直接呼叫即可:

1、工具類

package com.xxx.xxx.xxx;

import java.io.IOException;
import java.io.InputStream;
import
java.util.ArrayList; import java.util.List; import org.apache.poi.openxml4j.exceptions.InvalidFormatException; import org.apache.poi.openxml4j.exceptions.OpenXML4JException; import org.apache.poi.openxml4j.opc.OPCPackage; import org.apache.poi.xssf.eventusermodel.ReadOnlySharedStringsTable; import
org.apache.poi.xssf.eventusermodel.XSSFReader; import org.apache.poi.xssf.eventusermodel.XSSFSheetXMLHandler; import org.apache.poi.xssf.eventusermodel.XSSFSheetXMLHandler.SheetContentsHandler; import org.apache.poi.xssf.model.StylesTable; import org.apache.poi.xssf.usermodel.XSSFComment; import org.slf4j.Logger; import
org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import org.xml.sax.XMLReader; import org.xml.sax.helpers.XMLReaderFactory; /** * 解析大資料量Excel工具類 * @author RobinTime * */ @Component public class ExcelParser { private static final Logger logger = LoggerFactory.getLogger(ExcelParser.class); /** * 表格預設處理器 */ private ISheetContentHandler contentHandler = new DefaultSheetHandler(); /** * 讀取資料 */ private List<String[]> datas = new ArrayList<String[]>(); /** * 轉換表格,預設為轉換第一個表格 * @param stream * @return * @throws InvalidFormatException * @throws IOException * @throws ParseException */ public ExcelParser parse(InputStream stream) throws InvalidFormatException, IOException, ParseException { return parse(stream, 1); } /** * * @param stream * @param sheetId:為要遍歷的sheet索引,從1開始 * @return * @throws InvalidFormatException * @throws IOException * @throws ParseException */ public synchronized ExcelParser parse(InputStream stream, int sheetId) throws InvalidFormatException, IOException, ParseException { // 每次轉換前都清空資料 datas.clear(); // 開啟表格檔案輸入流 OPCPackage pkg = OPCPackage.open(stream); try { // 建立表閱讀器 XSSFReader reader; try { reader = new XSSFReader(pkg); } catch (OpenXML4JException e) { logger.error("讀取表格出錯"); throw new ParseException(e.fillInStackTrace()); } // 轉換指定單元表 InputStream shellStream = reader.getSheet("rId" + sheetId); try { InputSource sheetSource = new InputSource(shellStream); StylesTable styles = reader.getStylesTable(); ReadOnlySharedStringsTable strings = new ReadOnlySharedStringsTable(pkg); getContentHandler().init(datas);// 設定讀取出的資料 // 獲取轉換器 XMLReader parser = getSheetParser(styles, strings); parser.parse(sheetSource); } catch (SAXException e) { logger.error("讀取表格出錯"); throw new ParseException(e.fillInStackTrace()); } finally { shellStream.close(); } } finally { pkg.close(); } return this; } /** * 獲取表格讀取資料,獲取資料前,需要先轉換資料<br> * 此方法不會獲取第一行資料 * * @return 表格讀取資料 */ public List<String[]> getDatas() { return getDatas(true); } /** * 獲取表格讀取資料,獲取資料前,需要先轉換資料 * * @param dropFirstRow * 刪除第一行表頭記錄 * @return 表格讀取資料 */ public List<String[]> getDatas(boolean dropFirstRow) { if (dropFirstRow && datas.size() > 0) { datas.remove(0);// 刪除表頭 } return datas; } /** * 獲取讀取表格的轉換器 * * @return 讀取表格的轉換器 * @throws SAXException * SAX錯誤 */ protected XMLReader getSheetParser(StylesTable styles, ReadOnlySharedStringsTable strings) throws SAXException { XMLReader parser = XMLReaderFactory.createXMLReader(); parser.setContentHandler(new XSSFSheetXMLHandler(styles, strings, getContentHandler(), false)); return parser; } public ISheetContentHandler getContentHandler() { return contentHandler; } public void setContentHandler(ISheetContentHandler contentHandler) { this.contentHandler = contentHandler; } /** * 表格轉換錯誤 */ public class ParseException extends Exception { private static final long serialVersionUID = -2451526411018517607L; public ParseException(Throwable t) { super("表格轉換錯誤", t); } } public interface ISheetContentHandler extends SheetContentsHandler { /** * 設定轉換後的資料集,用於存放轉換結果 * * @param datas * 轉換結果 */ void init(List<String[]> datas); } /** * 預設表格解析handder */ class DefaultSheetHandler implements ISheetContentHandler { /** * 讀取資料 */ private List<String[]> datas; private int columsLength; // 讀取行資訊 private String[] readRow; private ArrayList<String> fristRow = new ArrayList<String>(); @Override public void init(List<String[]> datas) { this.datas = datas; // this.columsLength = columsLength; } @Override public void startRow(int rowNum) { if (rowNum != 0) { readRow = new String[columsLength]; } } @Override public void endRow(int rowNum) { //將Excel第一行表頭的列數當做陣列的長度,要保證後續的行的列數不能超過這個長度,這是個約定。 if (rowNum == 0) { columsLength = fristRow.size(); readRow = fristRow.toArray(new String[fristRow.size()]); }else { readRow = fristRow.toArray(new String[columsLength]); } datas.add(readRow.clone()); readRow = null; fristRow.clear(); } @Override public void cell(String cellReference, String formattedValue, XSSFComment comment) { int index = getCellIndex(cellReference);//轉換A1,B1,C1等表格位置為真實索引位置 try { fristRow.set(index, formattedValue); } catch (IndexOutOfBoundsException e) { int size = fristRow.size(); for (int i = index - size+1;i>0;i--){ fristRow.add(null); } fristRow.set(index,formattedValue); } } @Override public void headerFooter(String text, boolean isHeader, String tagName) { } /** * 轉換表格引用為列編號 * * @param cellReference * 列引用 * @return 表格列位置,從0開始算 */ public int getCellIndex(String cellReference) { String ref = cellReference.replaceAll("\\d+", ""); int num = 0; int result = 0; for (int i = 0; i < ref.length(); i++) { char ch = cellReference.charAt(ref.length() - i - 1); num = (int) (ch - 'A' + 1); num *= Math.pow(26, i); result += num; } return result - 1; } } }

2、呼叫

File tempFile = new File(this.getClass().getClassLoader().getResource("").getPath() + "tempFile\\" + (new Date()).getTime() + ".xlsx");
//傳入一個路徑產生流再將流傳入工具類,返回解析物件,Excel的所有資料就被解析到List<String[]> 裡面,遍歷list任由你處置。
FileInputStream inputStream = new FileInputStream(tempFile);
ExcelParser parse = excelParser.parse(inputStream);
List<String[]> datas = parse.getDatas();