PHPExcel用完了256,512和1024MB的内存
我不明白 XSLX表格大约有3MB,甚至1024MB的内存不足以让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表格读取数据到数组。
有什么build议么?
起初,我允许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
在PHPExcel论坛上有很多有关PHPExcel内存使用情况的文章。 所以通过以前的讨论可能会给你一些想法。 PHPExcel拥有电子表格的“内存”表示,并且容易受PHP内存限制。
文件的物理大小在很大程度上是不相关的,知道它包含多less个单元格(每个工作表上的行*列)就更重要了。
我一直使用的“经验法则”是平均约1k / cell,所以5M单元工作簿需要5GB的内存。 但是,有很多方法可以减less这个要求。 这些可以结合使用,具体取决于您在工作簿中需要访问哪些信息,以及要使用的信息。
如果您有多个工作表,但不需要加载所有工作表,则可以使用setLoadSheetsOnly()方法来限制Reader将加载的工作表。 加载一个命名工作表:
$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);
如果您只需要访问工作表的一部分,那么您可以定义一个读取filter来标识您实际上要加载的单元格:
$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);
使用读取filter,您也可以在“块”中读取工作簿,以便任何时候只有一个块是内存驻留:
$inputFileType = 'Excel5'; $inputFileName = './sampleData/example2.xls'; /** Define a Read Filter class implementing PHPExcel_Reader_IReadFilter */ 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; } } /** Create a new Reader of the type defined in $inputFileType **/ $objReader = PHPExcel_IOFactory::createReader($inputFileType); /** Define how many rows we want to read for each "chunk" **/ $chunkSize = 20; /** Create a new Instance of our Read Filter **/ $chunkFilter = new chunkReadFilter(); /** Tell the Reader that we want to use the Read Filter that we've Instantiated **/ $objReader->setReadFilter($chunkFilter); /** Loop to read our worksheet in "chunk size" blocks **/ /** $startRow is set to 2 initially because we always read the headings in row #1 **/ for ($startRow = 2; $startRow <= 65536; $startRow += $chunkSize) { /** Tell the Read Filter, the limits on which rows we want to read this iteration **/ $chunkFilter->setRows($startRow,$chunkSize); /** Load only the rows that match our filter from $inputFileName to a PHPExcel Object **/ $objPHPExcel = $objReader->load($inputFileName); // Do some processing here // Free up some of the memory $objPHPExcel->disconnectWorksheets(); unset($objPHPExcel); }
如果不需要加载格式化信息,只需要加载工作表数据,那么setReadDataOnly()方法将告诉读者只加载单元格值,忽略任何单元格格式:
$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);
使用单元格caching。 这是一种减less每个单元所需的PHP内存的方法,但速度却很快。 它通过以压缩格式存储单元格对象,或者在PHP内存之外(例如磁盘,APC,memcache)来存储单元格对象…但是存储的内存越多,脚本执行的速度就越慢。 但是,您可以将每个单元所需的内存减less到大约300字节,因此假设的5M单元需要大约1.4GB的PHP内存。
单元caching在开发者文档的第4.2.1节中描述
编辑
看看你的代码,你正在使用迭代器,这不是特别有效,并build立一个单元格数据的数组。 您可能需要查看已经内置到PHPExcel中的toArray()方法,并为您执行此操作。 还要看看最近关于新的变体方法rangeToArray()的讨论 ,以构build行数据的关联数组。
我有和PHPExcel相同的内存问题,实际上是所有其他的库。 马克·贝克build议可以解决这个问题(caching也可以),但是事实certificate,内存问题成了一个时间问题。 阅读和写作的时间对于大型电子表格来说是指数级的,所以不适合。
PHPExcel和其他人不是为了处理大文件,所以我创build了一个解决这个问题的库。 你可以在这里查看: https : //github.com/box/spout
希望有所帮助!
使用PHPExcel时,您可以采取很多措施来保留较less的内存。 我build议您在修改服务器在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(); 函数读取单元格时。
Ypu可以尝试PHP的Excel http://ilia.ws/archives/237-PHP-Excel-Extension-0.9.1.html它是一个PHP扩展和它的速度非常快。; (也比PHP实现使用更less的内存)
就我而言,phpexcel总是迭代19999行。 不pipe多less行实际上被填充。 所以100行数据总是以内存错误结束。
也许你只需要检查,如果当前行的单元格是空的,然后“继续”或者打破循环,迭代行。
只要转贴我的post从另一个线程。 它描述了不同的服务器生成或编辑Excel电子表格的方法,这些方法应该考虑在内。 对于大量的数据,我不会推荐像PHPExcel或ApachePOI(用于Java)的工具,因为它们的内存要求。 还有一种非常方便(虽然可能有点儿费力)的方式来将数据注入电子表格。 服务器生成或更新Excel电子表格可以实现简单的XML编辑。 您可以让XLSX电子表格坐在服务器上,每次从dB收集数据时,都使用php将其解压缩。 然后访问特定的XML文件,这些文件包含需要注入的工作表的内容并手动插入数据。 之后,您会压缩电子表格文件夹,以便将其作为常规XLSX文件进行分发。 整个过程相当快速和可靠。 很明显,与XLSX / Open XML文件的内部组织有关的问题和故障(例如,Excel趋向于将所有string存储在单独的表中,并在工作表文件中使用对该表的引用)。 但是当仅注入数字和string等数据时,并不那么困难。 如果有人有兴趣,我可以提供一些代码。
我碰到这个问题,不幸的是没有任何build议的解决scheme可以帮助我。 我需要PHPExcel提供的function(公式,条件样式等),所以使用不同的库不是一个选项。
我最终做的是把每个工作表写到一个单独的(临时)文件中,然后把这些单独的文件和我写的一些特殊的软件结合起来。 这将我的内存消耗从> 512 Mb减less到远低于100 Mb。 如果您有同样的问题,请参阅https://github.com/infostreams/excel-merge 。