sponsored links

阿里開源快速、簡單避免OOM的Excel處理工具

《開源精選》是我們分享Github、Gitee等開源社群中優質專案的欄目,包括技術、學習、實用與各種有趣的內容。本期推薦的是一個阿里開源基於Java的Excel解析工具——EasyExcel。

阿里開源快速、簡單避免OOM的Excel處理工具

Java解析、比較有名的框架有Apache poi、jxl,但他們都存在一個嚴重的問題就是消耗記憶體,poi有專門的SAX模式可以一定程度地解決一些記憶體問題,但poi還是有一些缺陷,比如部分版本Excel解壓縮以及解壓後儲存都是在記憶體中完成的,記憶體還是有很多消耗。easyexcel重寫了poi對Excel的解析,一個3M的Excel檔案使用poi解析仍然需要100M左右記憶體,改用easyexcel後可以降低到幾M,再大的excel也不會出現記憶體呼叫。

最新版本

<dependency>

<groupId>com.alibaba</groupId>

<artifactId>easyexcel</artifactId>

<version>3.0.5</version>

</dependency>

示例

  • 讀Excel

阿里開源快速、簡單避免OOM的Excel處理工具

物件

@Getter
@Setter
@EqualsAndHashCode
public class DemoData {
    private String string;
    private Date date;
    private Double doubleData;
}

監聽器

// 有個很重要的點 DemoDataListener 不能被spring管理,要每次讀取excel都要new,然後裡面用到spring可以構造方法傳進去
@Slf4j
public class DemoDataListener implements ReadListener<DemoData> {

    /**
     * 每隔5條儲存資料庫,實際使用中可以100條,然後清理list ,方便記憶體回收
     */
    private static final int BATCH_COUNT = 100;
    /**
     * 快取的資料
     */
    private List<DemoData> cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
    /**
     * 假設這個是一個DAO,當然有業務邏輯這個也可以是一個service。當然如果不用儲存這個物件沒用。
     */
    private DemoDAO demoDAO;

    public DemoDataListener() {
        // 這裡是demo,所以隨便new一個。實際使用如果到了spring,請使用下面的有參建構函式
        demoDAO = new DemoDAO();
    }

    /**
     * 如果使用了spring,請使用這個構造方法。每次建立Listener的時候需要把spring管理的類傳進來
     *
     * @param demoDAO
     */
    public DemoDataListener(DemoDAO demoDAO) {
        this.demoDAO = demoDAO;
    }

    /**
     * 這個每一條資料解析都會來呼叫
     *
     * @param data    one row value. Is is same as {@link AnalysisContext#readRowHolder()}
     * @param context
     */
    @Override
    public void invoke(DemoData data, AnalysisContext context) {
        log.info("解析到一條資料:{}", JSON.toJSONString(data));
        cachedDataList.add(data);
        // 達到BATCH_COUNT了,需要去儲存一次資料庫,防止資料幾萬條資料在記憶體,容易OOM
        if (cachedDataList.size() >= BATCH_COUNT) {
            saveData();
            // 儲存完成清理 list
            cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
        }
    }

    /**
     * 所有資料解析完成了 都會來呼叫
     *
     * @param context
     */
    @Override
    public void doAfterAllAnalysed(AnalysisContext context) {
        // 這裡也要儲存資料,確保最後遺留的資料也儲存到資料庫
        saveData();
        log.info("所有資料解析完成!");
    }

    /**
     * 加上儲存資料庫
     */
    private void saveData() {
        log.info("{}條資料,開始儲存資料庫!", cachedDataList.size());
        demoDAO.save(cachedDataList);
        log.info("儲存資料庫成功!");
    }
}

持久層

/**
 * 假設這個是你的DAO儲存。當然還要這個類讓spring管理,當然你不用需要儲存,也不需要這個類。
 **/
public class DemoDAO {
    public void save(List<DemoData> list) {
        // 如果是mybatis,儘量別直接呼叫多次insert,自己寫一個mapper裡面新增一個方法batchInsert,所有資料一次性插入
    }
}

最簡單的讀示例程式碼

    /**
     * 最簡單的讀
     * <p>
     * 1. 建立excel對應的實體物件 參照{@link DemoData}
     * <p>
     * 2. 由於預設一行行的讀取excel,所以需要建立excel一行一行的回撥監聽器,參照{@link DemoDataListener}
     * <p>
     * 3. 直接讀即可
     */
    @Test
    public void simpleRead() {
        // 寫法1:JDK8+ ,不用額外寫一個DemoDataListener
        // since: 3.0.0-beta1
        String fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx";
        // 這裡 需要指定讀用哪個class去讀,然後讀取第一個sheet 檔案流會自動關閉
        // 這裡每次會讀取3000條資料 然後返回過來 直接呼叫使用資料就行
        EasyExcel.read(fileName, DemoData.class, new PageReadListener<DemoData>(dataList -> {
            for (DemoData demoData : dataList) {
                log.info("讀取到一條資料{}", JSON.toJSONString(demoData));
            }
        })).sheet().doRead();

        // 寫法2:
        // 匿名內部類 不用額外寫一個DemoDataListener
        fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx";
        // 這裡 需要指定讀用哪個class去讀,然後讀取第一個sheet 檔案流會自動關閉
        EasyExcel.read(fileName, DemoData.class, new ReadListener<DemoData>() {
            /**
             * 單次快取的資料量
             */
            public static final int BATCH_COUNT = 100;
            /**
             *臨時儲存
             */
            private List<DemoData> cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);

            @Override
            public void invoke(DemoData data, AnalysisContext context) {
                cachedDataList.add(data);
                if (cachedDataList.size() >= BATCH_COUNT) {
                    saveData();
                    // 儲存完成清理 list
                    cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
                }
            }

            @Override
            public void doAfterAllAnalysed(AnalysisContext context) {
                saveData();
            }

            /**
             * 加上儲存資料庫
             */
            private void saveData() {
                log.info("{}條資料,開始儲存資料庫!", cachedDataList.size());
                log.info("儲存資料庫成功!");
            }
        }).sheet().doRead();

        // 有個很重要的點 DemoDataListener 不能被spring管理,要每次讀取excel都要new,然後裡面用到spring可以構造方法傳進去
        // 寫法3:
        fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx";
        // 這裡 需要指定讀用哪個class去讀,然後讀取第一個sheet 檔案流會自動關閉
        EasyExcel.read(fileName, DemoData.class, new DemoDataListener()).sheet().doRead();

        // 寫法4:
        fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx";
        // 一個檔案一個reader
        ExcelReader excelReader = null;
        try {
            excelReader = EasyExcel.read(fileName, DemoData.class, new DemoDataListener()).build();
            // 構建一個sheet 這裡可以指定名字或者no
            ReadSheet readSheet = EasyExcel.readSheet(0).build();
            // 讀取一個sheet
            excelReader.read(readSheet);
        } finally {
            if (excelReader != null) {
                // 這裡千萬別忘記關閉,讀的時候會建立臨時檔案,到時磁碟會崩的
                excelReader.finish();
            }
        }
    }

64M記憶體20秒讀取75M(46W行25列)的Excel:

阿里開源快速、簡單避免OOM的Excel處理工具

當然還有急速模式能更快,但是記憶體佔用會在100M多一點。

更多內容大家可自行前往閱讀。

開源地址:https://github.com/alibaba/easyexcel

分類: 文化
時間: 2021-11-12

相關文章

蔣勳先生品讀的《紅樓夢》跟我看的是同一本嗎?

蔣勳先生品讀的《紅樓夢》跟我看的是同一本嗎?
好像是十年前吧,在書店陪兒子看書,他看他的書,我到處溜達看.無意中看到一本<蔣勳細說紅樓夢>的書.再後來,據說有很多小資追捧. 他的書我無法看完,也看不下去.從一開始蔣勳說要把紅樓夢還原到 ...

歐陽奮強:紅樓夢的“寶二爺”,23歲爆紅時結婚,一生只愛一人

歐陽奮強:紅樓夢的“寶二爺”,23歲爆紅時結婚,一生只愛一人
歐陽奮強 "弱水三千,只取一瓢"."一生一世一雙人"在現在物慾縱橫的時代,總是帶有很多的美好與期待. 而現實中"渣男"."渣女&q ...

男子蝸居21年 只為復原《紅樓夢》
目前一個心懷夢想,蝸居出租屋的男子.為了再現<紅樓夢>80之後 原作者曹雪芹的文筆,大學畢業之後 就在湖南嶽麓山下租下一間8平方的出租屋在裡邊埋頭寫作21年,他沒結婚 日常生活也很簡單除了 ...

元春挺釵有太多不合理,背後藏著紅樓夢最大的政治秘密

元春挺釵有太多不合理,背後藏著紅樓夢最大的政治秘密
在木石前盟和金玉良緣之間,元春毫無疑問選擇後者.為了挺釵,甚至不惜違背身份,違背禮教,違背孝道. 看似冠冕堂皇的賢德妃,辦起事來卻處處都是禮教漏洞.政治bug.而這一切,都是為了薛寶釵. 元春戲份原本 ...

紅樓夢解讀1

紅樓夢解讀1
有一個想法,陸陸續續的寫一本自己關於<紅樓夢>的解讀(當然也參考和借鑑了各位前輩大家的觀點),不一定會完成,但慢慢寫著吧. 之前<金瓶梅>,我自己就完成了整本書的解讀,覺得挺滿 ...

《紅樓夢》告訴你,女人越活越好的3個跡象

《紅樓夢》告訴你,女人越活越好的3個跡象
邢岫煙,是大觀園裡的灰姑娘. <紅樓夢>第四十九回,漫天白雪,大觀園美得像個琉璃盒子.眾姐妹不是大紅猩猩氈,就是大紅羽緞,映著大雪,華麗風雅之極.只有邢岫煙家常舊衣,沒有擋雪的外套,凍得瑟 ...

如何快速理清紅樓夢中的輩分,才是看懂這本書的關鍵

如何快速理清紅樓夢中的輩分,才是看懂這本書的關鍵
紅樓夢中好幾百人物,大多有名有姓,且性格分明.如何理清他們之間的關係,是很多人讀這本書時的苦惱. 實際上,每本書都有一條主線.就如長江黃河一般,發源自青藏高原,但其中還有很多匯入的小河和分出去的支流, ...

87版《紅樓夢》金陵十二釵為什麼成為無法超越的經典?

87版《紅樓夢》金陵十二釵為什麼成為無法超越的經典?
金陵十二釵之林黛玉. 瀟湘妃子林黛玉在大觀園裡的才情是公認的好,嘆今生誰舍誰收?嫁與東風春不管,憑爾去,忍淹留.前身為絳珠仙草的她,用眼淚還盡了神瑛侍者寶玉的灌溉之恩.演員陳曉旭用實力和與生俱來的憂鬱 ...

紅樓夢:大事糊塗,小事狠毒,王夫人憑實力證明娶錯老婆毀三代

紅樓夢:大事糊塗,小事狠毒,王夫人憑實力證明娶錯老婆毀三代
<紅樓夢>中的王夫人是一個很關鍵的核心人物,她的戲份很多,身份複雜,又是榮國府的當家主母,可以說掌握著賈府的命脈,關聯幾個家族,也影響著書中一群主角的人生走向. 不幸的是王夫人是個糊塗人, ...

《紅樓夢》李紈寡嬸為何帶倆女兒進賈府?作者派她來教薛姨媽做人

《紅樓夢》李紈寡嬸為何帶倆女兒進賈府?作者派她來教薛姨媽做人
<紅樓夢>第四十九回,李紈的寡嬸突然不請自來,她帶著兩個女兒李紋.李綺來到賈府.此後一段時間裡,她成了賈母的座上賓,經常和薛姨媽同時出現在賈母面前陪聊說笑. 李嬸為什麼突然來京城?她帶著兩 ...

「曲靖文藝評論大賽」母愛深深深幾許——《紅樓夢》中幾位母親給現代家庭教育的啟示丨王惠瓊
來源:曲靖日報-掌上曲靖 母愛一直是人類歷史上一個最具魅力的詞彙.母愛是"辛勤三十日,母瘦雛漸肥"的辛勤哺育:是"臨行密密縫,意恐遲遲歸"的細膩周到:是&quo ...

《我愛我家》裡那麼多精彩臺詞,原來有這麼多都來自《紅樓夢》

《我愛我家》裡那麼多精彩臺詞,原來有這麼多都來自《紅樓夢》
#紅樓夢##我愛我家# 人到中年之後,很多事情都不願意嘗試了.費力氣的不願意,花時間的不願意,放在讀書上也是如此. 按理說讀書是應該讓人眼界開啟的,但很多時候我們會發現,年輕時候喜歡閱讀的領域會按照慣 ...

吳偉業與秣陵春在紅樓夢解讀裡的里程碑意義

吳偉業與秣陵春在紅樓夢解讀裡的里程碑意義
前面說了紅樓夢裡的空空道人是一個特殊的存在,紅樓夢的原作者不是空空道人,但他是一個非常重要的抄書人和增刪者,空空道人和現實世界裡的吳梅村具有同一性.吳梅村所撰的傳奇<秣陵春>,在這裡不摘錄 ...

樂韻去世26年,退出《紅樓夢》成就鄧婕,騙她的羅烈下場怎麼樣?

樂韻去世26年,退出《紅樓夢》成就鄧婕,騙她的羅烈下場怎麼樣?
1995年,28歲的樂(yue)韻從13樓一躍而下.在她去世26年之後,鄧婕的"王熙鳳"依然是後無來者的傳說,而最初這個角色可能是屬於樂韻的. 鄧婕 樂韻 若不是羅烈給樂韻編織了一 ...

《紅樓夢》看破未必就是放下

《紅樓夢》看破未必就是放下
最近又開始重看<紅樓夢>,細讀第一回,曹公說假作真時真亦假,個人拙見覺得,曹公經歷過家族由盛轉衰,在落魄潦草時落筆歷數往事,悔恨當初不聽勸,沒有好好讀書考取功名. 但他筆鋒一轉,說自己碌碌 ...

紅樓夢:賈瑞的悲劇是如何造成的?別把髒水都潑給王熙鳳

紅樓夢:賈瑞的悲劇是如何造成的?別把髒水都潑給王熙鳳
賈瑞是紅樓夢裡的大齡未婚男青年,原本只是小人物的他,卻因王熙鳳而被讀者所熟知. 賈瑞也是賈氏一族的子孫,因父母雙亡,跟著爺爺奶奶長大,這樣的身份,不由得令人同情. 都說窮人的孩子早當家,如果賈瑞肯下功 ...

紅樓夢:賈母不是狠心的狼外婆!她至少給林黛玉預留了三個後招

紅樓夢:賈母不是狠心的狼外婆!她至少給林黛玉預留了三個後招
<紅樓夢>中的賈母是林黛玉的外婆,自從賈敏過世,她就派人把林黛玉接到身邊親自教養,比對自己的親孫女都要好. 賈母開始一心想要撮合寶玉和黛玉的婚事,繼賈敏之後維繫和林家的姻親關係.但是林如海 ...

《紅樓夢》讀書筆記50

《紅樓夢》讀書筆記50
<紅樓夢>讀書筆記50 作者:一山晚啼 字數 :1630 本篇讀寫的是第一零三回,第一零四回. 第一零三回 施毒計金桂自焚身 昧真禪雨村空遇舊 第一零四回 醉金剛小鰍生大浪 痴公子餘痛觸前 ...

紅樓夢:詭異淒涼的中秋夜宴,預示賈府抄家日期和林黛玉魂歸時間

紅樓夢:詭異淒涼的中秋夜宴,預示賈府抄家日期和林黛玉魂歸時間
<紅樓夢>第七十五.七十六兩回,作者花費大篇幅筆墨,描寫了賈府的一場舉家團圓的中秋夜宴. 這一場宴會由賈母極力費心地親自操持主辦,賈府的老少爺們史無前例地齊聚一堂團團圓圓.但是,宴會氣氛十 ...

70歲賈平凹新作《暫坐》,為什麼被封“當代《紅樓夢》”

70歲賈平凹新作《暫坐》,為什麼被封“當代《紅樓夢》”
品味人生百味,一杯清茶暫坐. 1 賈平凹先生一直寫小說,到了70歲仍舊筆耕不輟.我看他的小說,也看了很多年,字裡行間透露黃土高坡的氣質,暢遊其中,彷彿是鄉間徒步旅行,節奏變慢,時光清淺. 不知不覺,賈 ...