PHPExcel的内存不足256、512和1024MB

PHPExcel的内存不足256、512和1024MB,php,phpexcel,Php,Phpexcel,我不明白。XSLX表大约有3MB大,但即使1024MB的RAM也不足以让PHPExcel将其加载到内存中 我可能在这里做了一些可怕的错误: function ReadXlsxTableIntoArray($theFilePath) { require_once('PHPExcel/Classes/PHPExcel.php'); $inputFileType = 'Excel2007'; $objReader = PHPExcel_IOFactory::createRead

我不明白。XSLX表大约有3MB大,但即使1024MB的RAM也不足以让PHPExcel将其加载到内存中

我可能在这里做了一些可怕的错误:

function ReadXlsxTableIntoArray($theFilePath)
{
    require_once('PHPExcel/Classes/PHPExcel.php');
    $inputFileType = 'Excel2007';
    $objReader = PHPExcel_IOFactory::createReader($inputFileType);
    $objReader->setReadDataOnly(true);
    $objPHPExcel = $objReader->load($theFilePath);
    $rowIterator = $objPHPExcel->getActiveSheet()->getRowIterator();
    $arrayData = $arrayOriginalColumnNames = $arrayColumnNames = array();
    foreach($rowIterator as $row){
        $cellIterator = $row->getCellIterator();
        $cellIterator->setIterateOnlyExistingCells(false); // Loop all cells, even if it is not set
        if(1 == $row->getRowIndex ()) {
            foreach ($cellIterator as $cell) {
                $value = $cell->getCalculatedValue();
                $arrayOriginalColumnNames[] = $value;
                // let's remove the diacritique
                $value = iconv('UTF-8', 'ISO-8859-1//TRANSLIT', $value);
                // and white spaces
                $valueExploded = explode(' ', $value);
                $value = '';
                // capitalize the first letter of each word
                foreach ($valueExploded as $word) {
                    $value .= ucfirst($word);
                }
                $arrayColumnNames[] = $value;
            }
            continue;
        } else {
            $rowIndex = $row->getRowIndex();
            reset($arrayColumnNames);
            foreach ($cellIterator as $cell) {
                $arrayData[$rowIndex][current($arrayColumnNames)] = $cell->getCalculatedValue();
                next($arrayColumnNames);
            }
        }
    }
    return array($arrayOriginalColumnNames, $arrayColumnNames, $arrayData);
}
上面的函数将数据从excel表格读取到数组中

有什么建议吗

起初,我允许PHP使用256MB的RAM。这还不够。然后,我将数量增加了一倍,然后还尝试了1024MB。由于出现以下错误,它的内存仍然不足:

Fatal error: Allowed memory size of 1073741824 bytes exhausted (tried to allocate 50331648 bytes) in D:\data\o\WebLibThirdParty\src\PHPExcel\Classes\PHPExcel\Reader\Excel2007.php on line 688

Fatal error (shutdown): Allowed memory size of 1073741824 bytes exhausted (tried to allocate 50331648 bytes) in D:\data\o\WebLibThirdParty\src\PHPExcel\Classes\PHPExcel\Reader\Excel2007.php on line 688

Ypu可以尝试PHP Excel,它是PHP的C扩展,速度非常快。(也比PHP实现使用更少的内存)

在PHPExcel论坛上有很多关于PHPExcel内存使用的文章;因此,通读之前的一些讨论可能会给你一些想法。PHPExcel拥有电子表格的“内存中”表示,并且容易受到PHP内存限制的影响

文件的物理大小在很大程度上是无关的。。。更重要的是要知道它包含多少单元格(每个工作表上的行*列)

我一直使用的“经验法则”是平均约1k/单元,因此一个5M单元的工作簿需要5GB的内存。然而,有许多方法可以减少这种需求。这些可以结合使用,具体取决于您需要在工作簿中访问哪些信息,以及您想对其执行什么操作

如果您有多个工作表,但不需要加载所有工作表,则可以使用setLoadSheetsOnly()方法限制读取器将加载的工作表。 要加载单个命名工作表,请执行以下操作:

$inputFileType = 'Excel5'; 
$inputFileName = './sampleData/example1.xls';
$sheetname = 'Data Sheet #2'; 
/**  Create a new Reader of the type defined in $inputFileType  **/
$objReader = PHPExcel_IOFactory::createReader($inputFileType);
/**  Advise the Reader of which WorkSheets we want to load  **/ 
$objReader->setLoadSheetsOnly($sheetname); 
/**  Load $inputFileName to a PHPExcel Object  **/
$objPHPExcel = $objReader->load($inputFileName);
或者,通过传递一个名称数组,一次调用setLoadSheetsOnly()可以指定多个工作表:

$inputFileType = 'Excel5'; 
$inputFileName = './sampleData/example1.xls';
$sheetnames = array('Data Sheet #1','Data Sheet #3'); 
/** Create a new Reader of the type defined in $inputFileType **/ 
$objReader = PHPExcel_IOFactory::createReader($inputFileType);
/** Advise the Reader of which WorkSheets we want to load **/ 
$objReader->setLoadSheetsOnly($sheetnames); 
/**  Load $inputFileName to a PHPExcel Object  **/
$objPHPExcel = $objReader->load($inputFileName);
如果只需要访问工作表的一部分,则可以定义一个读取筛选器,以确定实际要加载的单元格:

$inputFileType = 'Excel5'; 
$inputFileName = './sampleData/example1.xls';
$sheetname = 'Data Sheet #3'; 

/**  Define a Read Filter class implementing PHPExcel_Reader_IReadFilter  */ 
class MyReadFilter implements PHPExcel_Reader_IReadFilter {
    public function readCell($column, $row, $worksheetName = '') {
        //  Read rows 1 to 7 and columns A to E only 
        if ($row >= 1 && $row <= 7) {
           if (in_array($column,range('A','E'))) { 
              return true;
           }
        } 
        return false;
    }
}

/**  Create an Instance of our Read Filter  **/ 
$filterSubset = new MyReadFilter(); 
/** Create a new Reader of the type defined in $inputFileType **/ 
$objReader = PHPExcel_IOFactory::createReader($inputFileType);
/**  Advise the Reader of which WorkSheets we want to load 
     It's more efficient to limit sheet loading in this manner rather than coding it into a Read Filter  **/ 
$objReader->setLoadSheetsOnly($sheetname); 
echo 'Loading Sheet using filter';
/**  Tell the Reader that we want to use the Read Filter that we've Instantiated  **/ 
$objReader->setReadFilter($filterSubset); 
/**  Load only the rows and columns that match our filter from $inputFileName to a PHPExcel Object  **/
$objPHPExcel = $objReader->load($inputFileName);
使用单元缓存。这是一种减少每个单元格所需的PHP内存的方法,但要以速度为代价。它的工作原理是以压缩格式存储单元对象,或者在PHP内存之外(例如磁盘、APC、memcache)。。。但是,存储的内存越多,脚本执行的速度就越慢。但是,您可以将每个单元格所需的内存减少到大约300字节,因此假设的500个单元格将需要大约1.4GB的PHP内存

单元缓存在开发人员文档的第4.2.1节中进行了描述

编辑


查看您的代码,您正在使用迭代器,迭代器不是特别有效,并且正在构建一个单元格数据数组。您可能想看看toArray()方法,它已经内置在PHPExcel中,可以为您完成这项工作。另外,请看一下关于新变量方法rangeToArray()的介绍,该方法用于构建行数据的关联数组。

在我的例子中,phpexcel总是遍历19999行。不管实际填充了多少行。因此,100行数据总是以内存错误结束


也许您只需检查当前行中的单元格是否为空,然后“继续”或中断循环,这将迭代行。

只需从另一个线程重新发布我的帖子。它描述了服务器端生成或编辑Excel电子表格时应考虑的不同方法。对于大量数据,我不推荐使用PHPExcel或ApachePOI(用于Java)之类的工具,因为它们需要内存。还有另一种非常方便(尽管可能有点麻烦)的方法将数据注入电子表格。通过简单的XML编辑,可以在服务器端生成或更新Excel电子表格。您可以将XLSX电子表格放在服务器上,每次从dB收集数据时,您都可以使用php解压它。然后访问包含工作表内容的特定XML文件,这些工作表内容需要手动注入和插入数据。然后,压缩电子表格文件夹,以便将其作为常规XLSX文件分发。整个过程非常快速可靠。显然,XLSX/Open XML文件的内部组织几乎没有问题和小故障(例如,Excel倾向于将所有字符串存储在单独的表中,并在工作表文件中使用对此表的引用)。但当只注入数字和字符串之类的数据时,并没有那么难。如果有人感兴趣,我可以提供一些代码。

我在PHPExcel和其他所有库中都遇到了相同的内存问题。马克·贝克(MarkBaker)建议,分块读取数据可以解决这个问题(缓存也可以),但结果是内存问题变成了时间问题。读写时间是指数级的,所以对于大型电子表格来说,这不是一个好的选择

PHPExcel和其他工具不适合处理大文件,所以我创建了一个库来解决这个问题。您可以在这里查看:


希望有帮助

在使用PHPExcel时,您可以采取很多措施来节省内存。在Apache中修改服务器的内存限制之前,我建议您采取以下措施来优化内存使用

/* Use the setReadDataOnly(true);*/
    $objReader->setReadDataOnly(true);

/*Load only Specific Sheets*/
    $objReader->setLoadSheetsOnly( array("1", "6", "6-1", "6-2", "6-3", "6-4", "6-5", "6-6", "6-7", "6-8") );

/*Free memory when you are done with a file*/
$objPHPExcel->disconnectWorksheets();
   unset($objPHPExcel);
避免使用非常大的Exel文件,记住是文件大小导致进程运行缓慢和崩溃


避免使用getCalculatedValue();读取单元格时的函数。

我遇到了这个问题,不幸的是,没有任何建议的解决方案可以帮助我。我需要PHPExcel提供的功能(公式、条件样式等),因此使用不同的库不是一个选项


我最终做的是将每个工作表写入一个单独的(临时)文件,然后将这些单独的文件与我编写的一些特殊软件结合起来。这将我的内存消耗从>512 Mb减少到了100 Mb以下。看看你是否有同样的问题。

并且需要一个商业($199)componentIlia的代码是开源的,但它只是商业libX库()的PHP包装器。。。Ilia的包装器也是Linux专用的,还没有Windows或Mac版本,除非您准备使用com
$inputFileType = 'Excel5';
$inputFileName = './sampleData/example1.xls';
/** Create a new Reader of the type defined in $inputFileType **/ 
$objReader = PHPExcel_IOFactory::createReader($inputFileType);
/** Advise the Reader that we only want to load cell data, not formatting **/ 
$objReader->setReadDataOnly(true);
/**  Load $inputFileName to a PHPExcel Object  **/
$objPHPExcel = $objReader->load($inputFileName);
/* Use the setReadDataOnly(true);*/
    $objReader->setReadDataOnly(true);

/*Load only Specific Sheets*/
    $objReader->setLoadSheetsOnly( array("1", "6", "6-1", "6-2", "6-3", "6-4", "6-5", "6-6", "6-7", "6-8") );

/*Free memory when you are done with a file*/
$objPHPExcel->disconnectWorksheets();
   unset($objPHPExcel);