Java處理Excel文件工具包-easyexcel使用詳解

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();
        }
    }
}

 使用問題請與我反饋,謝謝優化

相關文章
相關標籤/搜索