POI 解析百万行 excel 的实现

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

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 回帖

相关帖子

欢迎来到这里!

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

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

推荐标签 标签

  • 运维

    互联网运维工作,以服务为中心,以稳定、安全、高效为三个基本点,确保公司的互联网业务能够 7×24 小时为用户提供高质量的服务。

    148 引用 • 257 回帖 • 1 关注
  • Windows

    Microsoft Windows 是美国微软公司研发的一套操作系统,它问世于 1985 年,起初仅仅是 Microsoft-DOS 模拟环境,后续的系统版本由于微软不断的更新升级,不但易用,也慢慢的成为家家户户人们最喜爱的操作系统。

    215 引用 • 462 回帖
  • Bug

    Bug 本意是指臭虫、缺陷、损坏、犯贫、窃听器、小虫等。现在人们把在程序中一些缺陷或问题统称为 bug(漏洞)。

    77 引用 • 1741 回帖 • 1 关注
  • Laravel

    Laravel 是一套简洁、优雅的 PHP Web 开发框架。它采用 MVC 设计,是一款崇尚开发效率的全栈框架。

    19 引用 • 23 回帖 • 684 关注
  • HHKB

    HHKB 是富士通的 Happy Hacking 系列电容键盘。电容键盘即无接点静电电容式键盘(Capacitive Keyboard)。

    5 引用 • 74 回帖 • 407 关注
  • SQLServer

    SQL Server 是由 [微软] 开发和推广的关系数据库管理系统(DBMS),它最初是由 微软、Sybase 和 Ashton-Tate 三家公司共同开发的,并于 1988 年推出了第一个 OS/2 版本。

    19 引用 • 31 回帖 • 3 关注
  • ActiveMQ

    ActiveMQ 是 Apache 旗下的一款开源消息总线系统,它完整实现了 JMS 规范,是一个企业级的消息中间件。

    19 引用 • 13 回帖 • 626 关注
  • TensorFlow

    TensorFlow 是一个采用数据流图(data flow graphs),用于数值计算的开源软件库。节点(Nodes)在图中表示数学操作,图中的线(edges)则表示在节点间相互联系的多维数据数组,即张量(tensor)。

    20 引用 • 19 回帖
  • Vditor

    Vditor 是一款浏览器端的 Markdown 编辑器,支持所见即所得、即时渲染(类似 Typora)和分屏预览模式。它使用 TypeScript 实现,支持原生 JavaScript、Vue、React 和 Angular。

    313 引用 • 1666 回帖 • 1 关注
  • Ngui

    Ngui 是一个 GUI 的排版显示引擎和跨平台的 GUI 应用程序开发框架,基于
    Node.js / OpenGL。目标是在此基础上开发 GUI 应用程序可拥有开发 WEB 应用般简单与速度同时兼顾 Native 应用程序的性能与体验。

    7 引用 • 9 回帖 • 346 关注
  • 博客

    记录并分享人生的经历。

    270 引用 • 2386 回帖
  • 深度学习

    深度学习(Deep Learning)是机器学习的分支,是一种试图使用包含复杂结构或由多重非线性变换构成的多个处理层对数据进行高层抽象的算法。

    40 引用 • 40 回帖
  • 持续集成

    持续集成(Continuous Integration)是一种软件开发实践,即团队开发成员经常集成他们的工作,通过每个成员每天至少集成一次,也就意味着每天可能会发生多次集成。每次集成都通过自动化的构建(包括编译,发布,自动化测试)来验证,从而尽早地发现集成错误。

    14 引用 • 7 回帖 • 2 关注
  • H2

    H2 是一个开源的嵌入式数据库引擎,采用 Java 语言编写,不受平台的限制,同时 H2 提供了一个十分方便的 web 控制台用于操作和管理数据库内容。H2 还提供兼容模式,可以兼容一些主流的数据库,因此采用 H2 作为开发期的数据库非常方便。

    11 引用 • 54 回帖 • 641 关注
  • Sublime

    Sublime Text 是一款可以用来写代码、写文章的文本编辑器。支持代码高亮、自动完成,还支持通过插件进行扩展。

    10 引用 • 5 回帖 • 2 关注
  • BND

    BND(Baidu Netdisk Downloader)是一款图形界面的百度网盘不限速下载器,支持 Windows、Linux 和 Mac,详细介绍请看这里

    107 引用 • 1281 回帖 • 23 关注
  • gRpc
    10 引用 • 8 回帖 • 54 关注
  • 生活

    生活是指人类生存过程中的各项活动的总和,范畴较广,一般指为幸福的意义而存在。生活实际上是对人生的一种诠释。生活包括人类在社会中与自己息息相关的日常活动和心理影射。

    228 引用 • 1450 回帖 • 1 关注
  • 小薇

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

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

    34 引用 • 467 回帖 • 693 关注
  • BAE

    百度应用引擎(Baidu App Engine)提供了 PHP、Java、Python 的执行环境,以及云存储、消息服务、云数据库等全面的云服务。它可以让开发者实现自动地部署和管理应用,并且提供动态扩容和负载均衡的运行环境,让开发者不用考虑高成本的运维工作,只需专注于业务逻辑,大大降低了开发者学习和迁移的成本。

    19 引用 • 75 回帖 • 619 关注
  • Wide

    Wide 是一款基于 Web 的 Go 语言 IDE。通过浏览器就可以进行 Go 开发,并有代码自动完成、查看表达式、编译反馈、Lint、实时结果输出等功能。

    欢迎访问我们运维的实例: https://wide.b3log.org

    30 引用 • 218 回帖 • 606 关注
  • VirtualBox

    VirtualBox 是一款开源虚拟机软件,最早由德国 Innotek 公司开发,由 Sun Microsystems 公司出品的软件,使用 Qt 编写,在 Sun 被 Oracle 收购后正式更名成 Oracle VM VirtualBox。

    10 引用 • 2 回帖 • 6 关注
  • Vue.js

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

    261 引用 • 662 回帖
  • 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 回帖 • 7 关注
  • Android

    Android 是一种以 Linux 为基础的开放源码操作系统,主要使用于便携设备。2005 年由 Google 收购注资,并拉拢多家制造商组成开放手机联盟开发改良,逐渐扩展到到平板电脑及其他领域上。

    333 引用 • 323 回帖 • 66 关注
  • 学习

    “梦想从学习开始,事业从实践起步” —— 习近平

    161 引用 • 473 回帖
  • Scala

    Scala 是一门多范式的编程语言,集成面向对象编程和函数式编程的各种特性。

    13 引用 • 11 回帖 • 107 关注