首页 >excel操作 > 内容

java解析/快速读取exce,提升性能,解决内存溢出(sax事件驱动解析excel),百万级数据处理,开箱即用。

2022年12月29日 23:03

10万行数据,80列,总共耗时12s。
经测试:入数据库140秒内【cpu 消耗 0.5核,内存消耗:150M内】
10万行数据 80列,耗时12秒在这里插入图片描述

jdk8
1,添加poi mavne依赖:

<dependency>            <groupId>org.apache.poi</groupId>            <artifactId>poi-ooxml</artifactId>            <version>3.17</version>        </dependency>        <dependency>            <groupId>org.apache.poi</groupId>            <artifactId>poi-ooxml-schemas</artifactId>            <version>3.17</version>        </dependency>        <dependency>            <groupId>org.apache.poi</groupId>            <artifactId>poi</artifactId>            <version>3.17</version>        </dependency>

2,创建目录 directory 并且在目录directory新建一个接口 BigReadExcelProcessData.java 再实现该接口,新建 BigReadExcelProcessimpl.java
① :接口BigReadExcelProcessData.java内容:

package com.murdock.examples.kuxingseng.directory;import java.util.Map;public interface BigReadExcelProcessData {    void processData(Map map);}

②:实现类BigReadExcelProcessimpl.java内容:

package com.murdock.examples.kuxingseng.directory.imp;import com.murdock.examples.kuxingseng.directory.BigReadExcelProcessData;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import java.util.Map;public class BigReadExcelProcessimpl implements BigReadExcelProcessData {    private final static Logger logger = LoggerFactory.getLogger(BigReadExcelProcessimpl.class);    @Override    public void processData(Map map) {        logger.info("map:" + map);    }}

3,封装excel 工具类,新建BigReadExcelUtils.java。

package com.murdock.examples.kuxingseng.utils;import com.murdock.examples.kuxingseng.directory.BigReadExcelProcessData;import org.apache.commons.lang.StringUtils;import org.apache.poi.openxml4j.opc.OPCPackage;import org.apache.poi.xssf.eventusermodel.XSSFReader;import org.apache.poi.xssf.model.SharedStringsTable;import org.apache.poi.xssf.usermodel.XSSFRichTextString;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.xml.sax.*;import org.xml.sax.helpers.DefaultHandler;import org.xml.sax.helpers.XMLReaderFactory;import java.io.InputStream;import java.util.*;import java.util.regex.Pattern;public class BigReadExcelUtils {    private final static Logger log = LoggerFactory.getLogger(BigReadExcelUtils.class);    private final int startRow;    private final int endRow;    private int currentRow = 0;    private final String filename;//文件的绝对路径    private BigReadExcelProcessData processData;    /**     * 构造方法     */    public BigReadExcelUtils(String pathFileName, BigReadExcelProcessData process) throws Exception {        if (StringUtils.isEmpty(pathFileName)) throw new Exception("file is null");        processData = process;        this.filename = pathFileName;        this.startRow = 0;        this.endRow = 100_0000;//设置默认最大解析100W行数据        processSheet();    }    /**     * 指定获取第一个sheet     */    private void processSheet() throws Exception {        OPCPackage pkg = OPCPackage.open(filename);        XSSFReader r = new XSSFReader(pkg);        SharedStringsTable sst = r.getSharedStringsTable();        XMLReader parser = fetchSheetParser(sst);        Iterator<InputStream> it = r.getSheetsData();        while (it.hasNext()) {            InputStream sheet1 = it.next();            InputSource sheetSource = new InputSource(sheet1);            parser.parse(sheetSource);            sheet1.close();        }    }    /**     * 加载sax 解析器     */    private XMLReader fetchSheetParser(SharedStringsTable sst) throws SAXException {        String saxParser = "com.sun.org.apache.xerces.internal.parsers.SAXParser";        XMLReader parser = XMLReaderFactory.createXMLReader(saxParser);        ContentHandler handler = new PagingHandler(sst);        parser.setContentHandler(handler);        return parser;    }    private class PagingHandler extends DefaultHandler {        private SharedStringsTable sst;        private String lastContents;        private boolean nextIsString;        private String index = null;        private Object NULL = null;        private Map<String, String> map = new HashMap<>(100);//初始化内存空间,性能优化,10W数据能减少30秒左右(受列影响)        private Pattern pattern = Pattern.compile("^A[0-9]+$");        private PagingHandler(SharedStringsTable table) {            this.sst = table;        }        /**         * 获取key 值         */        @Override        public void startElement(String uri, String localName, String name,                                 Attributes attributes) throws SAXException {            if (name.equals("c")) {                index = attributes.getValue("r");                //判断是否是新的一行                if (pattern.matcher(index).find()) {                    if (map != NULL && isAccess() && !map.isEmpty()) {                        processData.processData(map);                        map.clear();//使用完数据,清理数据                    }                    currentRow++;                }                if (isAccess()) {                    String cellType = attributes.getValue("t");                    if (cellType != null && cellType.equals("s")) {                        nextIsString = true;                    } else {                        nextIsString = false;                    }                }            }            lastContents = "";        }        /**         * 获取value         */        @Override        public void endElement(String uri, String localName, String name)                throws SAXException {            if (isAccess()) {                if (nextIsString) {                    int idx = Integer.parseInt(lastContents);                    lastContents = new XSSFRichTextString(sst.getEntryAt(idx)).toString();                    nextIsString = false;                }                if (name.equals("v")) {                    map.put(index, lastContents);                }            }        }        @Override        public void characters(char[] ch, int start, int length)                throws SAXException {            if (isAccess()) {                lastContents += new String(ch, start, length);            }        }        @Override        public void endDocument() throws SAXException {            if (map != null && isAccess() && !map.isEmpty()) {                processData.processData(map);                map.clear();            }        }    }    private boolean isAccess() {        if (currentRow >= startRow && startRow <= endRow) {            return true;        }        return false;    }}

以上代码可以直接使用亲测试通过,处理60M Excel占用内存260M 左右。

4,测试:

package com.murdock.examples.kuxingseng.directory.imp;import com.murdock.examples.kuxingseng.directory.BigReadExcelProcessData;import com.murdock.examples.kuxingseng.utils.BigReadExcelUtils;import org.junit.Test;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import static org.junit.Assert.*;public class BigReadExcelProcessimplTest {    private final static Logger logger = LoggerFactory.getLogger(BigReadExcelProcessimplTest.class);    @Test    public void processData() {        String filePath = "D:\\tmp\\demo.xlsx";        try {            BigReadExcelProcessData processData =  new BigReadExcelProcessimpl();            new BigReadExcelUtils(filePath, processData);        } catch (Exception e) {            logger.error("process faile.", e);        }    }}

结果:

18:58:43.416 [main] INFO com.murdock.examples.kuxingseng.directory.imp.BigReadExcelProcessimpl - map:{M1=测试13, I1=测试9, E1=测试5, A1=测试1, N1=测试14, J1=测试10, F1=测试6, B1=测试2, O1=测试15, K1=测试11, G1=测试7, C1=测试3, L1=测试12, H1=测试8, D1=测试4}18:58:43.422 [main] INFO com.murdock.examples.kuxingseng.directory.imp.BigReadExcelProcessimpl - map:{M2=测试13, I2=测试9, E2=测试5, A2=测试1, N2=测试14, J2=测试10, F2=测试6, B2=测试2, O2=测试15, K2=测试11, G2=测试7, C2=测试3, L2=测试12, H2=测试8, D2=测试4}18:58:43.424 [main] INFO com.murdock.examples.kuxingseng.directory.imp.BigReadExcelProcessimpl - map:{M3=测试13, I3=测试9, E3=测试5, A3=测试1, N3=测试14, J3=测试10, F3=测试6, B3=测试2, O3=测试15, K3=测试11, G3=测试7, C3=测试3, L3=测试12, H3=测试8, D3=测试4}


参考文章:https://blog.csdn.net/LB3701/article/details/106445529

郑重声明:本文版权归原作者所有,转载文章仅为传播更多信息之目的,如作者信息标记有误,请第一时候联系我们修改或删除,在此表示感谢。

特别提醒:

1、请用户自行保存原始数据,为确保安全网站使用完即被永久销毁,如何人将无法再次获取。

2、如果上次文件较大或者涉及到复杂运算的数据,可能需要一定的时间,请耐心等待一会。

3、请按照用户协议文明上网,如果发现用户存在恶意行为,包括但不限于发布不合适言论妄图

     获取用户隐私信息等行为,网站将根据掌握的情况对用户进行限制部分行为、永久封号等处罚。

4、如果文件下载失败可能是弹出窗口被浏览器拦截,点击允许弹出即可,一般在网址栏位置设置

5、欢迎将网站推荐给其他人,网站持续更新更多功能敬请期待,收藏网站高效办公不迷路。

      



登录后回复

共有0条评论