在 PHPExcel 论坛上有很多关于 PHPExcel 内存使用的文章;所以阅读之前的一些讨论可能会给你一些想法。PHPExcel 拥有电子表格的“内存中”表示,并且容易受到 PHP 内存限制的影响。
文件的物理大小在很大程度上无关紧要......了解它包含多少个单元格(每个工作表上的行*列)更为重要。
我一直使用的“经验法则”平均约为 1k/cell,因此 5M cell 的工作簿将需要 5GB 的内存。但是,您可以通过多种方式降低该要求。这些可以组合起来,具体取决于您需要在工作簿中访问哪些信息,以及您想用它做什么。
如果您有多个工作表,但不需要加载所有工作表,则可以使用 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);
如果您只需要访问工作表的一部分,那么您可以定义一个读取过滤器来识别您实际想要加载的单元格:
$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);
使用读取过滤器,您还可以读取“块”中的工作簿,以便在任何时候只有一个块驻留在内存中:
$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);
使用单元缓存。这是一种减少每个单元所需的 PHP 内存的方法,但会以速度为代价。它通过以压缩格式存储单元对象或在 PHP 内存(例如磁盘、APC、memcache)之外存储单元对象来工作……但是您节省的内存越多,脚本执行的速度就越慢。但是,您可以将每个单元所需的内存减少到大约 300 字节,因此假设的 5M 单元将需要大约 1.4GB 的 PHP 内存。
单元缓存在开发者文档的第 4.2.1 节中描述
编辑
查看您的代码,您正在使用不是特别有效的迭代器,并构建了一个单元格数据数组。您可能想查看已内置在 PHPExcel 中的 toArray() 方法,并为您执行此操作。还可以看看最近关于 SO 的讨论,关于新的变体方法 rangeToArray() 来构建行数据的关联数组。