【问题标题】:PHP - Zero length (empty) CSV but only for larger datasetsPHP - 零长度(空)CSV,但仅适用于较大的数据集
【发布时间】:2013-08-12 09:36:39
【问题描述】:

虽然我的应用程序是使用 Yii 框架构建的,但这更像是一个普遍的 PHP 问题(我认为)。

我有一些代码采用 Yii CActiveDataProvider,循环它并构建一个 CSV 导出。这很好用,并且可以正确构建和发送 CSV。

我在尝试导出更大的数据集时遇到问题。我已经成功输出了 ~2500 条记录,没有任何问题,但是当我为一组更大的数据(~5000 条记录)运行完全相同的代码时,脚本似乎运行正常,但发送了一个零长度/空白 CSV。我不知道为什么......它似乎运行了一段时间,然后发送 CSV,日志中没有错误或警告。可能是输出在准备好之前被刷新或类似的吗?

代码如下(为了清楚起见,这里添加了几个内联的 cmets):

<?php
header('Content-type: text/csv');
header('Content-Disposition: attachment; filename="vacancies.csv"');

set_time_limit(240); // I know this is arbitrarily long, it's just to avoid any timeout

$outstream = fopen("php://output", 'w');

$headings = array(
            $vacancy->getAttributeLabel('vacancy_id'), // this is a Yii method that returns the active record attribute as a string
            ...         
            );

fputcsv($outstream, $headings, ',', '"');

foreach($dp->getData() as $vacancy){ // the getData() method pulls the next active record model out of the Yii dataprovider and the values for various attributes are set below
    $row = array(
                $vacancy->vacancy_id,
                ...
                );

    fputcsv($outstream, $row, ',', '"');
}

fclose($outstream);
?>

对于为什么这在一定数量的记录下可以正常工作有什么想法吗?

更新 按照下面的建议重新检查日志后,我发现我实际上内存不足,doh!

我可以写出到文件系统,这可以让我获得大约 3000 条记录,但随后内存不足。知道更改代码以避免内存不足的最佳方法吗?

【问题讨论】:

  • 超时?超出内存?检查服务器日志
  • 您是否尝试过整理输出,然后传输标头(包括 Content-Length 标头)和数据?
  • 感谢您的回复,我重新检查了 apache 错误日志,确实看起来我写文件时内存不足。由于某种原因,我之前没有发现这一点。将文件写入磁盘可以让我在它死前获得大约 3000 条记录。现在我需要弄清楚如何在不耗尽内存的情况下写入文件!

标签: php csv yii export


【解决方案1】:

非常感谢检查错误日志的建议,我不知何故错过了我遇到的内存不足错误。

这个问题实际上是由我使用来自 Yii 框架的CActiveDataProvider 的方式引起的。正如我在问题中所做的那样,直接从 DataProvider 读取每一行到内存中,因为脚本在此运行意味着我最终用完了 PHP 可用的内存。

有两种方法可以解决这个问题,一种是在数据提供程序上将分页设置为较少数量的记录并手动迭代数据,每次迭代仅将页面大小加载到内存中。

我选择使用CDataProviderIterator 为我处理此问题$iterator = new CDataProviderIterator($dp); 这可以防止内存填满我正在检索的记录。

请注意,我还必须添加一个 ob_flush(); 调用以防止输出缓冲区被 CSV 内容 itemslf 填满。

作为参考,我最终得到了以下内容:

<?php
header('Content-type: text/csv');
header('Content-Disposition: attachment; filename="vacancies.csv"');

set_time_limit(240); 

$outstream = fopen("php://output", 'w');

$headings = array(
        $vacancy->getAttributeLabel('vacancy_id'), 
        ...         
        );

fputcsv($outstream, $headings, ',', '"');

$iterator = new CDataProviderIterator($dp); // create an iterator instead of just using the dataprovider

foreach($iterator  as $vacancy){ // use the new iterator here
    $row = array(
            $vacancy->vacancy_id,
            ...
            );

    fputcsv($outstream, $row, ',', '"');

    ob_flush(); // explicitly call a flush to avoid filling the buffer
}

fclose($outstream);
?>

如果没有这么多的建议,我不会想到要回去再次查看日志非常感谢:)

【讨论】:

  • 顺便说一句,接受你自己的答案绝对没有错,尤其是当它提供了这样的详细程度时。
  • 谢谢,因为我的帐户还很新,所以直到现在我才能接受它。现在已经这样做了:)
猜你喜欢
  • 2016-10-12
  • 2016-01-31
  • 2015-10-24
  • 1970-01-01
  • 2021-02-17
  • 1970-01-01
  • 2017-07-28
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多