easyexcel使用问题处理

项目中有处理excel文件需求,之前用过poi和jxl,两者处理文档的速度很快,但jxl无法处理07及以上版本的excel,而poi经常出现outofmemory错误,了解到阿里有一个开源的easyexcel可以解决poi中的oom问题,所以在项目中尝试使用easyexcel替代poi。

传送门:easyexcel

在实际使用过程中发现有几个地方有些小问题,一是当07版本excel文件中有多个sheet时,在获取指定的sheet中的数据时,指定sheet的顺序与实际取得的数据相反,如下获取第一个sheet中的数据,但是实际取得的是最后一个sheet的顺序

ExcelReader excelReader = new ExcelReader(new FileInputStream(excel), ExcelTypeEnum.XLSX, null, new ExcelListener());
excelReader.read(new Sheet(1, 1,Staff.class));

在查看插件源码后发现,它在获取sheet数据时,存放sheet的顺序不对,修改com.alibaba.excel.read.SaxAnalyserV07initSheetSourceList方法中的设置顺序时的代码即可,我这里是通过重新排序来修改顺序的,如下

    private void initSheetSourceList() throws IOException, ParserConfigurationException, SAXException {
        this.sheetSourceList = new ArrayList();
        InputStream workbookXml = new FileInputStream(this.workBookXMLFilePath);

        /* 重新获取sheet的个数 */
        /*sheetCount=0;
        Arrays.asList(new File(this.path+"/xl/worksheets/").listFiles()).stream().forEach((e)->{
            if(e.getName().endsWith(".xml"))
                sheetCount++;
        });*/

        XmlParserFactory.parse(workbookXml, new DefaultHandler() {
            @Override
            public void startElement(String uri, String localName, String qName, Attributes attrs) throws SAXException {
                if (qName.toLowerCase(Locale.US).equals("sheet")) {
                    String name = null;
                    int id = 0;

                    for (int i = 0; i < attrs.getLength(); i++) {
                        if (attrs.getLocalName(i).toLowerCase(Locale.US).equals("name")) {
                            name = attrs.getValue(i);
                        } else if (attrs.getLocalName(i).toLowerCase(Locale.US).equals("sheetid")) {
                            id = Integer.parseInt(attrs.getValue(i));
                            try {
                                InputStream inputStream = new FileInputStream(XMLTempFile.getSheetFilePath(path, id));
                                sheetSourceList.add(new SheetSource(id, name, inputStream));
                            } catch (FileNotFoundException e) {
                                e.printStackTrace();
                            }
                        }
                    }

                }
            }

        });
        workbookXml.close();
//        Collections.sort(sheetSourceList);
        Collections.sort(sheetSourceList, new Comparator() {
            @Override
            public int compare(SheetSource o1, SheetSource o2) {
                return o1.id - o2.id;
            }
        });

另一个问题是,当获取由easyexcel自己生成的excel文件中的数据时,报错问题,经检查,这是由于在上面的代码中设置id时,原先获取id的字段是"r:id",将这个改为"sheetid"即可,如上述代码。

easyexcel通过在用户文件夹中生成xml临时文件作为中转来获取或生成excel,经测试,完成后并不会删除临时文件,删除方法在com.alibaba.excel.read.SaxAnalyserV07中已定义,即

public void stop() {
    FileUtil.deletefile(path);
}

可以在完成(execute中finally处)后调用stop方法删除临时文件,其中在关闭时需要把所有已打开的inputstream全部关闭,源码中并没有对此作处理,导致可能出现文件删除不完全,如下

    @Override
    protected void execute() {
        try {
            Sheet sheet = analysisContext.getCurrentSheet();
            //设置了查询的sheet时
            if (!isAnalysisAllSheets(sheet)) {
                //当设置的sheetno为0时,不执行
                if (this.sheetSourceList.size() < sheet.getSheetNo() || sheet.getSheetNo() == 0) {
                    return;
                }
                //取当前已选择的sheet的数据
                InputStream sheetInputStream = this.sheetSourceList.get(sheet.getSheetNo() - 1).getInputStream();
                parseXmlSource(sheetInputStream);

                //关闭inputstream
                sheetInputStream.close();

                return;
            }
            //未设置sheetno时,取所有的数据
            int i = 0;
            for (SheetSource sheetSource : this.sheetSourceList) {
                i++;
                this.analysisContext.setCurrentSheet(new Sheet(i));
                InputStream sheetInputStream = sheetSource.getInputStream();
                parseXmlSource(sheetInputStream);

                //关闭inputstream
                sheetInputStream.close();
            }

        } catch (Exception e) {
            stop();
            throw new ExcelAnalysisException(e);
        } finally {
            //关闭列表中所有的inputstream
            sheetSourceList.stream().forEach((e)->{
                try {
                    if(e.getInputStream()!=null)
                        e.getInputStream().close();
                } catch (IOException e1) {
                    e1.printStackTrace();
                }
            });
            stop();
        }
    }

源码中删除文件的代码也要作修改,原来只删除了临时的文件而保留了文件夹,需要修改将文件夹也一并删除,代码在com.alibaba.excel.util.FileUtil.deletefile中,如下

    public static void deletefile(String delpath) {
        File file = new File(delpath);
        // 当且仅当此抽象路径名表示的文件存在且 是一个目录时,返回 true
        if (!file.isDirectory()) {
            file.delete();
        } else if (file.isDirectory()) {
            String[] filelist = file.list();
            for (int i = 0; i < filelist.length; i++) {
                File delfile = new File(delpath + File.separator + filelist[i]);
                if (!delfile.isDirectory()) {
                    delfile.delete();
                } else if (delfile.isDirectory()) {
                    deletefile(delpath + File.separator + filelist[i]);
                }
            }
            file.delete();
        }
    }

全部修改后重新打包即可。


因为easyexcel是通过SAX模式来处理excel文件的,不需要消耗很大的内存,可以避免poi中经常出现的oom错误,但是经测试,在处理稍大些的文件时,耗时很大,所以是否选用easyexcel还需要做一个取舍。

另人奇怪的是,测试中发现在处理03版本的文件时,easyexcel采用的还是poi的方式,所以处理速度很快,在使用过程中需要注意这一点。

 
 

你可能感兴趣的:(easyexcel,芊芊寻的随笔分享)