2

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

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

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

代码如下(为清楚起见,在此处添加了一些内联注释):

<?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 条记录,但随后内存不足。知道更改代码以避免内存不足的最佳方法吗?

4

1 回答 1

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);
?>

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

于 2013-08-12T12:17:43.807 回答