https://github.com/alibaba/easyexceljava
因爲項目須要對大量Excel數據進行輸入輸出處理,在使用JXL,POI後發現很容易出現OOM,最後在網上找到阿里的開源項目EasyExcel能很快速的讀取寫入超大Excel文件。通過大量的調試優化,現經過JAVA生成104萬行20列的數據並寫入到Excel文件的Sheet中只須要70秒的時間。git
如下爲本工程代碼:github
若是是maven工程在pom.xml中加入如下內容: 緩存
<dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>27.0-jre</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>easyexcel</artifactId> <version>1.1.2-beta5</version> </dependency>
工具包封裝maven
package utils; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.List; import java.util.Map; import java.util.Map.Entry; import com.alibaba.excel.EasyExcelFactory; import com.alibaba.excel.ExcelReader; import com.alibaba.excel.ExcelWriter; import com.alibaba.excel.context.AnalysisContext; import com.alibaba.excel.event.AnalysisEventListener; import com.alibaba.excel.metadata.BaseRowModel; import com.alibaba.excel.metadata.Sheet; import com.google.common.base.Splitter; import com.google.common.base.Strings; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * @author zhao.yingchao * @date 2019-02-27 * @since v1.0.0 */ public class EasyExcelUtil { private static final Logger LOGGER = LoggerFactory.getLogger(EasyExcelUtil.class); /** * 經過String類,讀取工做表數據 * * @param filePath 文件路徑 * @return 數據集 */ public static Map<String, List<List<String>>> readExcelByString(String filePath) { return readExcelByString(filePath, null); } /** * 經過String類,讀取工做表數據 * * @param filePath 文件路徑 * @param sheetName sheetName * @return 數據集合 */ public static Map<String, List<List<String>>> readExcelByString(String filePath, String sheetName) { // 建立返回信息 Map<String, List<List<String>>> dataListMap; // 解析監聽器 StringExcelListener excelListener = new StringExcelListener(); InputStream inputStream = null; try { // 建立文件流 inputStream = new FileInputStream(filePath); dataListMap = readExcelByStringFromInputStream(inputStream, sheetName); } catch (Exception e) { throw new EasyExcelException("readExcelByModel from filePath failed." + e, e); } finally { // 關閉文件流 try { if (null != inputStream) { inputStream.close(); } } catch (IOException e) { LOGGER.error("inputStream.close failed!", e); } } return dataListMap; } /** * 經過String類,讀取工做表數據 * * @param inputStream 文件流 * @param sheetName sheetName * @return 數據集合 */ public static Map<String, List<List<String>>> readExcelByStringFromInputStream(InputStream inputStream, String sheetName) { // 建立返回信息 Map<String, List<List<String>>> dataListMap = Maps.newLinkedHashMap(); // 解析監聽器 StringExcelListener excelListener = new StringExcelListener(); try { // 建立文件流 ExcelReader excelReader = EasyExcelFactory.getReader(inputStream, excelListener); // 獲得全部工做表 List<Sheet> sheets = excelReader.getSheets(); // 取全部工做表數據 for (Sheet sheet : sheets) { // 工做表名稱 String currentSheetName = sheet.getSheetName(); // 沒有指定工做表,或多個工做表 if (Strings.isNullOrEmpty(sheetName) || Splitter.on(',').trimResults().omitEmptyStrings().splitToList( sheetName).contains(currentSheetName)) { // 讀取Excel數據 excelReader.read(sheet); // 返回明細數據 List<List<String>> sheetDataInfo = Lists.newArrayList(excelListener.getDataList()); // 將工做表數據放入工做薄 dataListMap.put(currentSheetName, sheetDataInfo); // 清除緩存數據 excelListener.clear(); } } } catch (Exception e) { throw new EasyExcelException("readExcelByStringFromInputStream from inputStream failed." + e, e); } return dataListMap; } /** * 經過Model類,讀取工做表數據 * * @param filePath 文件路徑 * @param clazz BaseRowModel * @return 數據集合 */ public static Map<String, List<? extends BaseRowModel>> readExcelByModel(String filePath, Class<? extends BaseRowModel> clazz) { return readExcelByModel(filePath, null, clazz); } /** * 經過Model類,讀取工做表數據 * * @param file 文件 * @param clazz BaseRowModel * @return 數據集合 */ public static Map<String, List<? extends BaseRowModel>> readExcelByModel(File file, Class<? extends BaseRowModel> clazz) { return readExcelByModel(file, null, clazz); } /** * 經過Model類,讀取工做表數據 * * @param filePath 文件路徑 * @param sheetName sheetName * @param clazz BaseRowModel * @return 數據集合 */ public static List<? extends BaseRowModel> readExcelByModelSheetName(String filePath, String sheetName, Class<? extends BaseRowModel> clazz) { Map<String, List<? extends BaseRowModel>> dataListMap = readExcelByModel(filePath, sheetName, clazz); return dataListMap.getOrDefault(sheetName, null); } /** * 經過Model類,讀取工做表數據 * * @param file 文件 * @param sheetName sheetName * @param clazz BaseRowModel * @return 數據集合 */ public static List<? extends BaseRowModel> readExcelByModelSheetName(File file, String sheetName, Class<? extends BaseRowModel> clazz) { Map<String, List<? extends BaseRowModel>> dataListMap = readExcelByModel(file, sheetName, clazz); return dataListMap.getOrDefault(sheetName, null); } /** * 經過Model類,讀取工做表數據 * * @param filePath 文件路徑 * @param sheetName sheetName * @param clazz BaseRowModel * @return 數據集合 */ public static Map<String, List<? extends BaseRowModel>> readExcelByModel(String filePath, String sheetName, Class<? extends BaseRowModel> clazz) { Map<String, List<? extends BaseRowModel>> dataListMap; InputStream inputStream = null; try { // 建立文件流 inputStream = new FileInputStream(filePath); dataListMap = readExcelByModelFromInputStream(inputStream, sheetName, clazz); } catch (Exception e) { throw new EasyExcelException("readExcelByModel from filePath failed." + e, e); } finally { // 關閉文件流 try { if (null != inputStream) { inputStream.close(); } } catch (IOException e) { LOGGER.error("inputStream.close failed!", e); } } return dataListMap; } /** * 經過Model類,讀取工做表數據 * * @param file 文件 * @param sheetName sheetName * @param clazz BaseRowModel * @return 數據集合 */ public static Map<String, List<? extends BaseRowModel>> readExcelByModel(File file, String sheetName, Class<? extends BaseRowModel> clazz) { Map<String, List<? extends BaseRowModel>> dataListMap; InputStream inputStream = null; try { // 建立文件流 inputStream = new FileInputStream(file); dataListMap = readExcelByModelFromInputStream(inputStream, sheetName, clazz); // 關閉文件流 inputStream.close(); } catch (Exception e) { throw new EasyExcelException("readExcelByModel from File failed." + e, e); } finally { // 關閉文件流 try { if (null != inputStream) { inputStream.close(); } } catch (IOException e) { LOGGER.error("inputStream.close failed!", e); } } return dataListMap; } /** * 經過Model類,讀取工做表數據 * * @param inputStream 文件流 * @param sheetName sheetName * @param clazz BaseRowModel * @return 數據集合 */ public static Map<String, List<? extends BaseRowModel>> readExcelByModelFromInputStream(InputStream inputStream, String sheetName, Class<? extends BaseRowModel> clazz) { // 解析每行結果在listener中處理 // 建立返回信息 Map<String, List<? extends BaseRowModel>> dataListMap = Maps.newLinkedHashMap(); // 解析監聽器 ModelExcelListener excelListener = new ModelExcelListener(); try { // 建立文件流 ExcelReader excelReader = EasyExcelFactory.getReader(inputStream, excelListener); // 獲得全部工做表 List<Sheet> sheets = excelReader.getSheets(); // 取全部工做表數據 for (Sheet sheet : sheets) { // 工做表名稱 String currentSheetName = sheet.getSheetName(); if (Strings.isNullOrEmpty(sheetName) || Splitter.on(',').trimResults().omitEmptyStrings().splitToList( sheetName).contains(currentSheetName)) { // 設置模板 sheet.setClazz(clazz); // 讀取Excel數據 excelReader.read(sheet); // 返回明細數據 List<? extends BaseRowModel> sheetDataInfo = Lists.newArrayList(excelListener.getDataList()); // 將工做表數據放入工做薄 dataListMap.put(currentSheetName, sheetDataInfo); // 清除緩存數據 excelListener.clear(); } } } catch (Exception e) { throw new EasyExcelException("readExcelByModel from inputStream failed." + e, e); } return dataListMap; } /** * 經過String類,將一個sheet寫入到一個Excel * * @param filePath 文件路徑 * @param sheetName sheetName * @param dataList 數據集 */ public static void writeExcelByString(String filePath, String sheetName, List<List<String>> dataList) { // 建立返回信息 Map<String, List<List<String>>> dataListMap = Maps.newLinkedHashMap(); // 將工做表放入到Excel中 dataListMap.put(sheetName, dataList); // 輸出Excel數據 writeExcelByString(filePath, dataListMap); } /** * 經過String類,將多個sheet寫入到一個Excel * * @param filePath 文件路徑 * @param dataListMap 數據集 */ public static void writeExcelByString(String filePath, Map<String, List<List<String>>> dataListMap) { try { // 工做表編號 int sheetNo = 1; // 建立文件流 OutputStream out = new FileOutputStream(filePath); ExcelWriter writer = EasyExcelFactory.getWriter(out); // 循環寫入每一個工做表 for (Entry<String, List<List<String>>> entry : dataListMap.entrySet()) { // 獲得工做表名稱 String sheetName = entry.getKey(); // 獲得工做表數據 List<List<String>> dataList = entry.getValue(); // 設置工做表信息 Sheet sheet1 = new Sheet(sheetNo++, 1, null, sheetName, null); // 設置開始行爲-1 sheet1.setStartRow(-1); // 設置自適應寬度 sheet1.setAutoWidth(Boolean.TRUE); // 開始寫數據 writer.write0(dataList, sheet1); } // 清空緩存 writer.finish(); // 關閉文件 out.close(); } catch (Exception e) { throw new EasyExcelException("writeExcelByString failed." + e, e); } } /** * 經過Model類,將一個sheet寫入到一個Excel * * @param filePath 文件路徑 * @param sheetName sheetName * @param dataList 數據集 * @param clazz BaseRowModel */ public static void writeExcelByModel(String filePath, String sheetName, List<? extends BaseRowModel> dataList, Class<? extends BaseRowModel> clazz) { // 建立返回信息 Map<String, List<? extends BaseRowModel>> dataListMap = Maps.newLinkedHashMap(); // 將工做表放入到Excel中 dataListMap.put(sheetName, dataList); // 輸出Excel數據 writeExcelByModel(filePath, dataListMap, clazz); } /** * 經過String類,將多個sheet寫入到一個Excel * * @param filePath 文件路徑 * @param dataListMap 數據集 * @param clazz BaseRowModel */ public static void writeExcelByModel(String filePath, Map<String, List<? extends BaseRowModel>> dataListMap, Class<? extends BaseRowModel> clazz) { try { // 建立文件流 OutputStream out = new FileOutputStream(filePath); // 寫入文件 writeIntoOutputStream(dataListMap, clazz, out); // 關閉文件 out.close(); } catch (Throwable e) { throw new EasyExcelException("write to file failed." + e, e); } } /** * export to byte array. * * @param sheetName sheetName * @param dataList data * @param clazz BaseRowModel * @return return a byte array with data. */ public static byte[] exportByteArray(String sheetName, List<? extends BaseRowModel> dataList, Class<? extends BaseRowModel> clazz) { // 建立返回信息 Map<String, List<? extends BaseRowModel>> dataListMap = Maps.newLinkedHashMap(); // 將工做表放入到Excel中 dataListMap.put(sheetName, dataList); ByteArrayOutputStream out = new ByteArrayOutputStream(); writeIntoOutputStream(dataListMap, clazz, out); return out.toByteArray(); } /** * export to byte array. * * @param dataListMap data * @param clazz BaseRowModel * @return return a byte array with data. */ public static byte[] exportByteArray(Map<String, List<? extends BaseRowModel>> dataListMap, Class<? extends BaseRowModel> clazz) { ByteArrayOutputStream out = new ByteArrayOutputStream(); writeIntoOutputStream(dataListMap, clazz, out); return out.toByteArray(); } /** * 數據寫入輸出流 * * @param dataListMap 數據 * @param clazz BaseRowModel * @param outputStream 輸出流 */ public static void writeIntoOutputStream(Map<String, List<? extends BaseRowModel>> dataListMap, Class<? extends BaseRowModel> clazz, OutputStream outputStream) { try { // 工做表編號 int sheetNo = 1; // 建立文件流 ExcelWriter writer = EasyExcelFactory.getWriter(outputStream); // 循環寫入每一個工做表 for (Entry<String, List<? extends BaseRowModel>> entry : dataListMap.entrySet()) { // 獲得工做表名稱 String sheetName = entry.getKey(); // 獲得工做表數據 List<? extends BaseRowModel> dataList = entry.getValue(); // 設置工做表信息 Sheet sheet1 = new Sheet(sheetNo++, 1, clazz, sheetName, null); // 設置自適應寬度 sheet1.setAutoWidth(Boolean.TRUE); // 開始寫數據 writer.write(dataList, sheet1); } // 清空緩存 writer.finish(); } catch (Throwable e) { throw new EasyExcelException("write to OutputStream failed." + e, e); } } /** * String類,解析監聽器 */ private static class StringExcelListener extends AnalysisEventListener<List<String>> { /** * 自定義用於暫時存儲data 能夠經過實例獲取該值 */ private List<List<String>> dataList = Lists.newArrayList(); @Override public void invoke(List<String> rowInfo, AnalysisContext context) { // 數據存儲到list,供批量處理,或後續本身業務邏輯處理。 dataList.add(rowInfo); } @Override public void doAfterAllAnalysed(AnalysisContext context) { //解析結束銷燬不用的資源 } private List<List<String>> getDataList() { return dataList; } private void setDataList(List<List<String>> dataList) { this.dataList = dataList; } private void clear() { dataList.clear(); } } /** * Model類,解析監聽器 */ private static class ModelExcelListener extends AnalysisEventListener<BaseRowModel> { /** * 自定義用於暫時存儲data 能夠經過實例獲取該值 */ private List<BaseRowModel> dataList = Lists.newArrayList(); @Override public void invoke(BaseRowModel rowInfo, AnalysisContext context) { dataList.add(rowInfo); } /** * 解析結束銷燬不用的資源 * * @param context AnalysisContext */ @Override public void doAfterAllAnalysed(AnalysisContext context) { //解析結束銷燬不用的資源 } /** * 獲取 * * @return 返回sheet數據 */ private List<? extends BaseRowModel> getDataList() { return dataList; } /** * 設置sheet數據 * * @param dataList 數據 */ private void setDataList(List<BaseRowModel> dataList) { this.dataList = dataList; } /** * 清空數據 */ private void clear() { dataList.clear(); } } /** * EasyExcelException */ public static class EasyExcelException extends RuntimeException { private static final long serialVersionUID = -5456062088984840434L; public EasyExcelException() { super(); } public EasyExcelException(String message) { super(message); } public EasyExcelException(String message, Throwable cause) { super(message, cause); } public EasyExcelException(Throwable cause) { super(cause); } } }
樣例:ide
/** * Excel模型 * * @author zhao.yingchao * @date 2019年02月27日 * @since v1.0.0 */ @Data public class Test extends BaseRowModel { @ExcelProperty(value = "姓名", index = 0) private String name; @ExcelProperty(value = "年齡", index = 1) private String age; @ExcelProperty(value = "手機號", index = 2) private String phoneNum; }
使用:工具
/** * @author zhao.yingchao * @date 2019-02-27 * @since v1.0.0 */ public class EasyExcelUtilTest { @Test public void test_parse_excel() { Resource resource = new ClassPathResource("file/list1W.xlsx"); try { long startTime = MonitorUtil.startLogInfo("parse begin"); List<Test> list = (List<Test>)EasyExcelUtil.readExcelByModelSheetName(resource.getFile(), "Sheet1", Test.class); MonitorUtil.endLogInfo(startTime, "parse end"); Assert.assertEquals(list.size(), 10000); } catch (IOException e) { e.printStackTrace(); } } }
使用問題請與我反饋,謝謝優化