【问题标题】:phpexcel memory exhausted with 128Mb memory reading only first row of a big filephpexcel内存耗尽,128Mb内存仅读取大文件的第一行
【发布时间】:2015-09-15 17:20:12
【问题描述】:

我有一个大约 95.500 行和 28 列的 xlsx 文件的内存问题。 为了处理这么大的文件(超过 10 MB xlsx),我写了下面的代码,但是当我执行代码并调用加载方法时,即使只读取了一行,我也会收到内存耗尽错误! (我只分配了 128Mb 给 php 解释器)

请考虑:

  • 目前我尝试仅读取一行并收到有关内存耗尽的错误(请参阅 $chunkFilter->setRows(1,1);)

  • 解决了关于读取第一行的问题后,我需要读取所有其他行以将内容加载到数据库表中

  • 如果您认为有其他库或解决方案,请考虑我更喜欢 PHP 作为语言,因为它是此应用程序使用的主要语言,但我可以接受任何其他语言的解决方案(如 go)

请不要简单地建议增加 php 进程的内存。我已经知道这是可能的,但这段代码在 VPS 共享服务器上运行,最大只有 512Mb 的 RAM,我需要尽可能降低内存使用率

有解决办法吗?请在下面找到我使用的代码:

/**  Define a Read Filter class implementing PHPExcel_Reader_IReadFilter  to read file in "chunks" */
class chunkReadFilter implements PHPExcel_Reader_IReadFilter {
    private $_startRow = 0;
    private $_endRow = 0;

     /**  Set the list of rows that we want to read  */
     public function setRows($startRow, $chunkSize) {
       $this->_startRow    = $startRow;
       $this->_endRow      = $startRow + $chunkSize;
   }

public function readCell($column, $row, $worksheetName = '') {
    //  Only read the heading row, and the rows that are configured in $this->_startRow and $this->_endRow
    if (($row == 1) || ($row >= $this->_startRow && $row < $this->_endRow)) {
           return true;
       }
       return false;
    }
}

function loadXLSFile($inputFile){
   // Initiate cache
   $cacheMethod = PHPExcel_CachedObjectStorageFactory:: cache_to_sqlite3;    
   if (!PHPExcel_Settings::setCacheStorageMethod($cacheMethod)) {
       echo date('H:i:s'), " Unable to set Cell Caching using ", $cacheMethod,
                                     " method, reverting to memory", EOL;
   }
    $inputFileType = PHPExcel_IOFactory::identify($inputFile);
    $objReader = PHPExcel_IOFactory::createReader($inputFileType);
    $chunkFilter = new chunkReadFilter();
    //  Tell the Read Filter, the limits on which rows we want to read this iteration
    $chunkFilter->setRows(1,1);
    // Tell the Reader that we want to use the Read Filter that we've Instantiated
    $objReader->setReadFilter($chunkFilter);
    $objReader->setReadDataOnly(true);
    $objPHPExcel = $objReader->load($inputFile);
}

更新

pamelus要求返回的错误下方

PHP Fatal error:  Allowed memory size of 134217728 bytes exhausted (tried to allocate 112 bytes) in /vendor/phpoffice/phpexcel/Classes/PHPExcel/Reader/Excel2007.php on line 471
PHP Stack trace:
PHP   1. {main}() dataimport.php:0
PHP   2. loadFileToDb($inputFile = *uninitialized*, $tabletoupdate = *uninitialized*) dataimport.php:373
PHP   3. PHPExcel_Reader_Excel2007->load($pFilename = *uninitialized*) dataimport.php:231

【问题讨论】:

  • 您能否发布您收到的错误消息?或者至少是它出现的行号?
  • 嗨,pamelus,感谢您对我的问题感兴趣,请在更新的问题上找到确切的错误消息

标签: php memory phpexcel


【解决方案1】:

鉴于您的内存限制较低,我可以建议您使用 PHPExcel 的替代方案来一劳永逸地解决您的问题:Spout。它只需要 10MB 内存,所以你应该很好!

您的loadXLSXFile() 函数将变为:

use Box\Spout\Reader\ReaderFactory;
use Box\Spout\Common\Type;

function loadXLSFile($inputFile) {
    $reader = ReaderFactory::create(Type::XLSX);
    $reader->open($inputFile);

    foreach ($reader->getSheetIterator() as $sheet) {
        foreach ($sheet->getRowIterator() as $row) {
            // $row is the first row of the sheet. Do something with it
            break; // you won't read any other rows
        }
        break; // if you only want to read the first sheet
    }

    $reader->close();
}

就这么简单!无需缓存、过滤器和其他优化:)

【讨论】:

  • 嗨 Adrien,谢谢你的建议,但据我所知 Spout 不支持公式,对吗?有时我的文件将包含某些字段的公式(只需 % 增量)
  • XLSX 文件应包含给定公式的计算值。因此,即使单元格包含=A1+B1 之类的内容,它也包含结果。 Spout 应该能够读取并返回这个结果。
猜你喜欢
  • 1970-01-01
  • 2020-06-25
  • 2011-04-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-04-28
  • 2018-10-17
  • 2017-06-23
相关资源
最近更新 更多