POI 解析百万行 excel 的实现

本贴最后更新于 2486 天前,其中的信息可能已经东海扬尘

poi 的usermodel api读取大数据量excel会导致OOM,可以使用eventmodel api来处理这种excel.


少量的行数excel可以用


XSSFWorkbook wb = new XSSFWorkbook(inputStream);  
         XSSFSheet sheet = wb.getSheetAt(0);  
         Iterator<Row> iter = sheet.iterator();  
         boolean isfirstline = true;  
         while (iter.hasNext()) {  
             Row row = iter.next();  
             if (isfirstline) { // 忽略上传文件第一行的标题栏  
                 isfirstline = false;  
                 continue;  
             }  
              //解析excel,每行有11列,然后对每列解析出来之后调用后端服务把数据保存到数据库中,  
             }  
         } 
 


但是大于几十万行的时候就会出现 out of memory的错误。很简单
XSSFWorkbook wb = new XSSFWorkbook(inputStream);  

导致了内存溢出。 
解决方法就是可以使用eventmodel api来处理这种excel.
new ExcelEventUserModelParse(filePath)
                    .setHandler(new ExcelEventUserModelParse.SimpleSheetContentsHandler() {
                        @Override
                        public void endRow(int rowNum) {
                            // System.out.println(row.toString());
                            if (!isEmpty.getIsEmpty()) {
                                table.add(row);
                            } else {
                                isnull.setIsEmpty(isEmpty.getIsEmpty());
                                isnull.setRowNum(isEmpty.getRowNum());
                            }
                        }
                    }).parse();

table就是解析出来的结果
public class ExcelEventUserModelParse {
    private Logger logger = LoggerFactory.getLogger(ExcelEventUserModelParse.class);
    private String filename;
    private SheetContentsHandler handler;
public ExcelEventUserModelParse(String filename) {
    this.filename = filename;
}


public ExcelEventUserModelParse setHandler(SheetContentsHandler handler) {
    this.handler = handler;
    return this;
}


public void parse() {
    OPCPackage pkg = null;
    InputStream sheetInputStream = null;
    try {
        pkg = OPCPackage.open(filename, PackageAccess.READ);
        XSSFReader xssfReader = new XSSFReader(pkg);
        StylesTable styles = xssfReader.getStylesTable();
        ReadOnlySharedStringsTable strings = new ReadOnlySharedStringsTable(pkg);
        sheetInputStream = xssfReader.getSheetsData().next();
        processSheet(styles, strings, sheetInputStream);
    } catch (Exception e) {
        logger.error(e.getMessage());
        throw new RuntimeException(e.getMessage(), e);
    } finally {
        if (sheetInputStream != null) {
            try {
                sheetInputStream.close();
            } catch (IOException e) {
                logger.error(e.getMessage());
                throw new RuntimeException(e.getMessage(), e);
            }
        }
        if (pkg != null) {
            try {
                pkg.close();
            } catch (IOException e) {
                logger.error(e.getMessage());
                throw new RuntimeException(e.getMessage(), e);
            }
        }
    }
}


private void processSheet(StylesTable styles, ReadOnlySharedStringsTable strings, InputStream sheetInputStream)
        throws SAXException, ParserConfigurationException, IOException {
    XMLReader sheetParser = SAXHelper.newXMLReader();


    if (handler != null) {
        sheetParser.setContentHandler(new XSSFSheetXMLHandler(styles, strings, handler, false));
    } else {
        sheetParser.setContentHandler(
                new XSSFSheetXMLHandler(styles, strings, new SimpleSheetContentsHandler(), false));
    }


    sheetParser.parse(new InputSource(sheetInputStream));
}


public static class SimpleSheetContentsHandler implements SheetContentsHandler {
    private Logger logger = LoggerFactory.getLogger(SimpleSheetContentsHandler.class);
    protected ImportDecryptModel row = new ImportDecryptModel();
    protected int a = -1;
    protected JudgementModel isEmpty = new JudgementModel();


    @Override
    public void startRow(int rowNum) {
        if (rowNum == a + 1) {
            a = rowNum;
        } else {
            isEmpty.setIsEmpty(true);
            logger.error("第" + rowNum + "行数据为空");
            a = rowNum;
            isEmpty.setRowNum(rowNum);
        }
        row = new ImportDecryptModel();
    }


    @Override
    public void endRow(int rowNum) {
        logger.error(rowNum + " : " + row);
    }


    @Override
    public void cell(String cellReference, String formattedValue, XSSFComment comment) {
        if (isEmpty != null &amp;&amp; !isEmpty.getIsEmpty()) {
            switch (cellReference.substring(0, 1)) {
            case "A":
                row.setUserId(formattedValue);
                break;
            case "B":
                row.setUid(formattedValue);
                break;
            case "C":
                row.setUserName(formattedValue);
                break;
            case "D":
                row.setIdCard(formattedValue);
                break;
            case "E":
                row.setMobileNo(formattedValue);
                break;
            case "F":
                row.setBankCard(formattedValue);
                break;
            default:
                break;
            }
        }
    }


    @Override
    public void headerFooter(String text, boolean isHeader, String tagName) {


    }
}</pre>

解析的工作全部在cell()里面,速度大约100万条记录20s左右。关键不会内存溢出

  • POI
    22 引用 • 21 回帖

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...

推荐标签 标签

  • 小薇

    小薇是一个用 Java 写的 QQ 聊天机器人 Web 服务,可以用于社群互动。

    由于 Smart QQ 从 2019 年 1 月 1 日起停止服务,所以该项目也已经停止维护了!

    34 引用 • 467 回帖 • 693 关注
  • 人工智能

    人工智能(Artificial Intelligence)是研究、开发用于模拟、延伸和扩展人的智能的理论、方法、技术及应用系统的一门技术科学。

    75 引用 • 145 回帖 • 1 关注
  • PostgreSQL

    PostgreSQL 是一款功能强大的企业级数据库系统,在 BSD 开源许可证下发布。

    22 引用 • 22 回帖
  • Sillot

    Sillot (汐洛)孵化自思源笔记,致力于服务智慧新彖乄,具有彖乄驱动、极致优雅、开发者友好的特点
    Github 地址:https://github.com/Hi-Windom/Sillot

    15 引用 • 6 回帖 • 28 关注
  • Latke

    Latke 是一款以 JSON 为主的 Java Web 框架。

    70 引用 • 532 回帖 • 712 关注
  • Jenkins

    Jenkins 是一套开源的持续集成工具。它提供了非常丰富的插件,让构建、部署、自动化集成项目变得简单易用。

    51 引用 • 37 回帖
  • Logseq

    Logseq 是一个隐私优先、开源的知识库工具。

    Logseq is a joyful, open-source outliner that works on top of local plain-text Markdown and Org-mode files. Use it to write, organize and share your thoughts, keep your to-do list, and build your own digital garden.

    4 引用 • 55 回帖 • 10 关注
  • CSDN

    CSDN (Chinese Software Developer Network) 创立于 1999 年,是中国的 IT 社区和服务平台,为中国的软件开发者和 IT 从业者提供知识传播、职业发展、软件开发等全生命周期服务,满足他们在职业发展中学习及共享知识和信息、建立职业发展社交圈、通过软件开发实现技术商业化等刚性需求。

    14 引用 • 155 回帖
  • ZooKeeper

    ZooKeeper 是一个分布式的,开放源码的分布式应用程序协调服务,是 Google 的 Chubby 一个开源的实现,是 Hadoop 和 HBase 的重要组件。它是一个为分布式应用提供一致性服务的软件,提供的功能包括:配置维护、域名服务、分布式同步、组服务等。

    59 引用 • 29 回帖 • 18 关注
  • 链滴

    链滴是一个记录生活的地方。

    记录生活,连接点滴

    131 引用 • 3639 回帖
  • 架构

    我们平时所说的“架构”主要是指软件架构,这是有关软件整体结构与组件的抽象描述,用于指导软件系统各个方面的设计。另外还有“业务架构”、“网络架构”、“硬件架构”等细分领域。

    140 引用 • 441 回帖 • 1 关注
  • Vue.js

    Vue.js(读音 /vju ː/,类似于 view)是一个构建数据驱动的 Web 界面库。Vue.js 的目标是通过尽可能简单的 API 实现响应的数据绑定和组合的视图组件。

    261 引用 • 662 回帖
  • 反馈

    Communication channel for makers and users.

    123 引用 • 906 回帖 • 193 关注
  • HTML

    HTML5 是 HTML 下一个的主要修订版本,现在仍处于发展阶段。广义论及 HTML5 时,实际指的是包括 HTML、CSS 和 JavaScript 在内的一套技术组合。

    103 引用 • 294 回帖
  • Caddy

    Caddy 是一款默认自动启用 HTTPS 的 HTTP/2 Web 服务器。

    10 引用 • 54 回帖 • 126 关注
  • Openfire

    Openfire 是开源的、基于可拓展通讯和表示协议 (XMPP)、采用 Java 编程语言开发的实时协作服务器。Openfire 的效率很高,单台服务器可支持上万并发用户。

    6 引用 • 7 回帖 • 89 关注
  • LeetCode

    LeetCode(力扣)是一个全球极客挚爱的高质量技术成长平台,想要学习和提升专业能力从这里开始,充足技术干货等你来啃,轻松拿下 Dream Offer!

    209 引用 • 72 回帖 • 2 关注
  • TextBundle

    TextBundle 文件格式旨在应用程序之间交换 Markdown 或 Fountain 之类的纯文本文件时,提供更无缝的用户体验。

    1 引用 • 2 回帖 • 47 关注
  • Hexo

    Hexo 是一款快速、简洁且高效的博客框架,使用 Node.js 编写。

    21 引用 • 140 回帖 • 28 关注
  • WebClipper

    Web Clipper 是一款浏览器剪藏扩展,它可以帮助你把网页内容剪藏到本地。

    3 引用 • 9 回帖 • 6 关注
  • PHP

    PHP(Hypertext Preprocessor)是一种开源脚本语言。语法吸收了 C 语言、 Java 和 Perl 的特点,主要适用于 Web 开发领域,据说是世界上最好的编程语言。

    164 引用 • 407 回帖 • 526 关注
  • 音乐

    你听到信仰的声音了么?

    59 引用 • 509 回帖
  • MySQL

    MySQL 是一个关系型数据库管理系统,由瑞典 MySQL AB 公司开发,目前属于 Oracle 公司。MySQL 是最流行的关系型数据库管理系统之一。

    675 引用 • 535 回帖 • 1 关注
  • 锤子科技

    锤子科技(Smartisan)成立于 2012 年 5 月,是一家制造移动互联网终端设备的公司,公司的使命是用完美主义的工匠精神,打造用户体验一流的数码消费类产品(智能手机为主),改善人们的生活质量。

    4 引用 • 31 回帖 • 10 关注
  • 快应用

    快应用 是基于手机硬件平台的新型应用形态;标准是由主流手机厂商组成的快应用联盟联合制定;快应用标准的诞生将在研发接口、能力接入、开发者服务等层面建设标准平台;以平台化的生态模式对个人开发者和企业开发者全品类开放。

    15 引用 • 127 回帖 • 2 关注
  • ZeroNet

    ZeroNet 是一个基于比特币加密技术和 BT 网络技术的去中心化的、开放开源的网络和交流系统。

    1 引用 • 21 回帖 • 592 关注
  • Solo

    Solo 是一款小而美的开源博客系统,专为程序员设计。Solo 有着非常活跃的社区,可将文章作为帖子推送到社区,来自社区的回帖将作为博客评论进行联动(具体细节请浏览 B3log 构思 - 分布式社区网络)。

    这是一种全新的网络社区体验,让热爱记录和分享的你不再感到孤单!

    1425 引用 • 10043 回帖 • 470 关注