如何修复使用PHPExcel时内存耗尽的问题?

27

致命错误:内存大小达到了134217728字节的限制(尝试分配1078799字节),位于D:\xampplite\htdocs\Scraper\PHPExcel\Reader\Excel2007.php的第269行。

即使我只是尝试打开一个大约350KB大小的Excel文件,使用PHPExcel库,我的128M PHP内存限制很快就会被耗尽。

虽然可以通过更改配置来增加内存限制,但如果有其他的解决方案修复这个问题将会更好。


4
找出 PHP Excel 中占用大量内存的部分,然后修复它。 - SoapBox
2
@SoapBox:简而言之,PHPExcel是一个真正的内存占用大户 :( 此外,它是一种相当复杂的库,因此在其中找到(更不用说修复)错误是相当困难的。在最坏的情况下,可能更容易绕过这个问题并找到一些替代库。 - Piskvor left the building
3
成为微软认证专业人员,只需要知道如何重新启动计算机。这将释放泄漏软件正在使用的内存。 - Chuck Burgess
1
@SoapBox - 如果有人能帮我找到一种减少PHPExcel要求的方法(而不会使其变得无法使用),那么我很乐意实现它。 - Mark Baker
8个回答

65

使用 PHPExcel 时,文件大小不是衡量工作簿文件的好指标;行数和列数(即单元格数)更重要。

PHPExcel 代码本身的占用空间在 10 到 25MB 之间,取决于正在访问哪些组件。

目前,每个工作簿中的单元格平均占用 1k 的内存(没有任何缓存),或者在 64 位 PHP 上为 1.6k - 我假设此处使用的是 32 位 PHP。例如,一个包含 8000 行和 31 列(248,000 个单元格)的工作表将需要大约 242MB 的空间。使用单元格缓存(如 php://temp 或 DiskISAM),可以将其减少到大约三分之一,因此 8000 行乘以 31 列将需要大约 80MB 的空间。

有许多选项可帮助您减少内存使用:

您是否正在使用 PHPExcel 的单元格缓存?

require_once './Classes/PHPExcel.php';

$cacheMethod = PHPExcel_CachedObjectStorageFactory:: cache_to_phpTemp;
$cacheSettings = array( ' memoryCacheSize ' => '8MB');
PHPExcel_Settings::setCacheStorageMethod($cacheMethod, $cacheSettings);

$objReader = PHPExcel_IOFactory::createReader('Excel2007');
$objPHPExcel = $objReader->load("test.xlsx");

如果您只需要访问工作表中的数据,而不需要访问单元格格式,则可以禁用从工作簿读取格式信息的功能:

$objReader = PHPExcel_IOFactory::createReader('Excel2007');
$objReader->setReadDataOnly(true);
$objPHPExcel = $objReader->load("test.xlsx");

如果你只需要访问工作簿中的部分工作表,而不是全部,那么你可以仅加载这些工作表:

$objReader = PHPExcel_IOFactory::createReader('Excel2007');
$objReader->setLoadSheetsOnly( array("Worksheet1", "Worksheet2") );
$objPHPExcel = $objReader->load("test.xlsx");

如果你只想读取工作表中的特定单元格,可以添加筛选器:

class MyReadFilter implements PHPExcel_Reader_IReadFilter
{
    public function readCell($column, $row, $worksheetName = '') {
        // Read title row and rows 20 - 30
        if ($row == 1 || ($row >= 20 && $row <= 30)) {
            return true;
        }

        return false;
    }
}

$objReader = PHPExcel_IOFactory::createReader('Excel2007');
$objReader->setReadFilter( new MyReadFilter() );
$objPHPExcel = $objReader->load("test.xlsx");

所有这些技术都可以显著减少内存需求。


五年之后,你的回答还是最新的吗? - robsch
1
@robsch - Excel文件的格式已经有5年没有改变了;PHP内存限制在5年后仍然是PHP内存限制;单元格缓存和读取过滤器仍然存在于PHPExcel中。 - Mark Baker
作家怎么样?我试过读者,但它不起作用。 - Rohit

6

根据链接的网站,“Spreadsheet_Excel_Writer已经过时,需要完全重写。如果您想帮助完成此任务,请与我们联系。否则,我们不建议使用此软件包进行新开发。” - Isius
5
写作:PHP_XLSXWriter 的内存使用量比PHPExcel低95%。 - velcrow

3
只因数据文件只有X个字节,并不意味着它使用X个字节的RAM。例如,$_SESSION数组中仅有4K的数据在加载时使用了64K的RAM。这取决于代码对该数据的处理方式。正确的答案是增加RAM的数量。
另外,如果这是一个XLSX文件,它们是ZIP'd XML文档。文本文件的压缩比1/2要高得多,因此您的350K XLSX文件很容易成为1MB的Excel文件。

1

如果您想找出原因,可以使用xhprof。根据该链接,您可以使用它跟踪内存使用情况...


1

Xdebug是一种用于PHP的分析器/调试器,它可以帮助您跟踪内存使用和功能调用,以找出问题所在。而且它很容易安装,大多数Linux发行版都在库中提供了“yum install xdebug”、“apt-get install xdebug”。


1

还有一件事情需要注意,那就是空单元格。

我曾经遇到过同样的问题,只有800行数据,脚本甚至无法读取。 当超时或内存不足错误被修复后,两者都会出现。

@Mark Baker提到的关于单元格的某些内容让我开始思考。我注意到我有很多空单元格,只将带有数据的单元格复制到新工作簿中,就可以在一秒钟内运行。

希望这能帮助到某些人。


0
$objReader = PHPExcel_IOFactory::createReader('Excel2007');
$objReader->setReadDataOnly(true);
$objPHPExcel = $objReader->load("test.xlsx");

这样的代码已经足够了


0
这篇文章讲述了如何关闭一个PHPExcel读取器: 如何在php-excel-reader中关闭Excel文件 如果您同时打开多个Excel文件,但实际上并不需要它们始终保持打开状态,则可以尝试逐个打开和关闭(即取消设置)每个文件的读取器。顺便说一句,对于“取消设置”操作,使用相同的变量名称即可。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接