【问题标题】:Apache POI Java Excel Performance for Large Spreadsheets大型电子表格的 Apache POI Java Excel 性能
【发布时间】:2011-05-13 13:23:32
【问题描述】:

我有一个试图用 POI 读取的电子表格(我有 xls 和 xlsx 格式),但在这种情况下,问题出在 xls 文件上。我的电子表格有大约 10,000 行和 75 列,读取它可能需要几分钟(尽管 Excel 会在几秒钟内打开)。我正在使用基于事件的读取,而不是将整个文件读入内存。我的代码的内容如下。现在有点乱,但实际上只是一个很长的 switch 语句,主要是从 POI 示例中复制的。

使用事件模型的 POI 性能通常如此缓慢吗?我有什么办法可以加快速度吗?我认为几分钟对我的申请来说是不可接受的。

    POIFSFileSystem poifs = new POIFSFileSystem(fis);
    InputStream din = poifs.createDocumentInputStream("Workbook");
    try
    {
        HSSFRequest req = new HSSFRequest();
        listener = new FormatTrackingHSSFListener(new HSSFListener() {
            @Override
            public void processRecord(Record rec)
            {
                thisString = null;
                int sid = rec.getSid();
                switch (sid)
                {
                    case SSTRecord.sid:
                        strTable = (SSTRecord) rec;
                        break;
                    case LabelSSTRecord.sid:
                        LabelSSTRecord labelSstRec = (LabelSSTRecord) rec;
                        thisString = strTable.getString(labelSstRec
                                .getSSTIndex()).getString();
                        row = labelSstRec.getRow();
                        col = labelSstRec.getColumn();
                        break;
                    case RKRecord.sid:
                        RKRecord rrk = (RKRecord) rec;
                        thisString = "";
                        row = rrk.getRow();
                        col = rrk.getColumn();
                        break;
                    case LabelRecord.sid:
                        LabelRecord lrec = (LabelRecord) rec;
                        thisString = lrec.getValue();
                        row = lrec.getRow();
                        col = lrec.getColumn();
                        break;
                    case BlankRecord.sid:
                        BlankRecord blrec = (BlankRecord) rec;
                        thisString = "";
                        row = blrec.getRow();
                        col = blrec.getColumn();
                        break;
                    case BoolErrRecord.sid:
                        BoolErrRecord berec = (BoolErrRecord) rec;
                        row = berec.getRow();
                        col = berec.getColumn();
                        byte errVal = berec.getErrorValue();
                        thisString = errVal == 0 ? Boolean.toString(berec
                                .getBooleanValue()) : ErrorConstants
                                .getText(errVal);
                        break;
                    case FormulaRecord.sid:
                        FormulaRecord frec = (FormulaRecord) rec;
                        switch (frec.getCachedResultType())
                        {
                            case Cell.CELL_TYPE_NUMERIC:
                                double num = frec.getValue();
                                if (Double.isNaN(num))
                                {
                                    // Formula result is a string
                                    // This is stored in the next record
                                    outputNextStringRecord = true;
                                }
                                else
                                {
                                    thisString = formatNumericValue(frec, num);
                                }
                                break;
                            case Cell.CELL_TYPE_BOOLEAN:
                                thisString = Boolean.toString(frec
                                        .getCachedBooleanValue());
                                break;
                            case Cell.CELL_TYPE_ERROR:
                                thisString = HSSFErrorConstants
                                        .getText(frec.getCachedErrorValue());
                                break;
                            case Cell.CELL_TYPE_STRING:
                                outputNextStringRecord = true;
                                break;
                        }
                        row = frec.getRow();
                        col = frec.getColumn();
                        break;
                    case StringRecord.sid:
                        if (outputNextStringRecord)
                        {
                            // String for formula
                            StringRecord srec = (StringRecord) rec;
                            thisString = srec.getString();
                            outputNextStringRecord = false;
                        }
                        break;
                    case NumberRecord.sid:
                        NumberRecord numRec = (NumberRecord) rec;
                        row = numRec.getRow();
                        col = numRec.getColumn();
                        thisString = formatNumericValue(numRec, numRec
                                .getValue());
                        break;
                    case NoteRecord.sid:
                        NoteRecord noteRec = (NoteRecord) rec;
                        row = noteRec.getRow();
                        col = noteRec.getColumn();
                        thisString = "";
                        break;
                    case EOFRecord.sid:
                        inSheet = false;
                }
                if (thisString != null)
                {
                    // do something with the cell value 
                }
            }
        });
        req.addListenerForAllRecords(listener);
        HSSFEventFactory factory = new HSSFEventFactory();
        factory.processEvents(req, din);

【问题讨论】:

  • 是 xls 速度慢而 xlsx 工作正常吗?对我来说恰恰相反,写一个多行的文件只为 xls 使用几兆字节的堆,但对于 xlsx,即使 2GB 也不够。如果输出超过 X 行,我需要回退到 xls
  • @rlovtang,xlsx 更慢。我的意思是我使用 POI 而不是 JExcel 之类的东西,因为我需要同时处理 xls 和 xlsx。

标签: java performance excel apache-poi


【解决方案1】:

如果您使用 Apache POI 生成大型 excel 文件,请注意以下行:

sheet.autoSizeColumn((short) p);

因为这会降低性能。

【讨论】:

  • 它将如何影响性能?它会帮助还是会阻碍它?
  • 这真的帮了我大忙,我运行一个简单的 3000 行输出需要 10 分钟到 2 秒(字面意思)。您会注意到这个问题,因为您的代码在写出时变慢了。谢谢@sams
【解决方案2】:

我还对数千个大型 Excel 文件进行了一些处理,我认为 POI 非常快。在 Excel 本身中加载该 excel 文件也需要大约 1 分钟。所以我会确认问题出在 POI 代码上

【讨论】:

    【解决方案3】:

    我会尝试使用 poi-beta3 中引入的流式 hssf。这有助于解决具有 1000 多列的大型电子表格的内存问题。

    【讨论】:

      【解决方案4】:

      如果您使用 Apache POI 生成大型 excel 文件,请注意 sheet.autoSizeColumn((short) p);行,因为这会影响性能。

      http://stanicblog.blogspot.sg/2013/07/generate-large-excel-report-by-using.html

      【讨论】:

      • 这可能不是真的,因为在文章中 autoSizeColumn 是在添加每一行时循环调用的,而不是在所有行都完成后循环调用。
      【解决方案5】:

      我做了一些更详细的分析,看起来问题实际上出在 POI 之外的代码中。我只是假设这是瓶颈,但我认为这是不正确的。

      【讨论】:

      • 你能提供更多关于代码在哪里的细节吗?是您自己的代码还是第三方 jars?
      • 这是我自己的代码。我错误地尝试对文本字段进行一些非常低效的解析,以查看它们是否具有日期值(当用户将文本日期输入到没有日期格式的单元格中时)。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-30
      • 1970-01-01
      • 1970-01-01
      • 2017-04-18
      相关资源
      最近更新 更多