使用StreamedResponse下载CSV在几行之后停止


Downloading CSV with StreamedResponse stops after several rows

我有一个MongoDB集合,里面有几千个条目,我想作为CSV文件下载。

我有一些代码,基本上可以完成以下操作;控制器方法使用条令ODM查询数据库中的所有记录。然后,返回的游标被馈送到StreamedResponse。在StreamedResponse中,我在光标上循环,并将每条记录输出为CSV中的一行。

以下代码有效,并下载了一个文件。仅包含不超过60行。当光标计数指示有2670个结果时,它停止流式传输的原因是什么?

// Get all available bookings
$cursor = $this
        ->get('doctrine_mongodb')
        ->getRepository('FooBundle:Booking')
        ->getQueryBuilder()
        ->getQuery()
        ->execute();
$response = new StreamedResponse(function () use ($cursor) {
    $handle = fopen('php://output', 'r+');
    $headerPrinted = false;
    // Calling $cursor->count() here returns 2670
    foreach ($cursor as $result) {
        // Transform the Booking object to a flat array.
        $data = $this->constructRow($result);
        // Print CSV header
        if (!$headerPrinted) {
            fputcsv($handle, array_keys($data), ';', '"');
            $headerPrinted = true;
        }
        // Add a line in the csv file.
        fputcsv($handle, $data, ';', '"');
    }
    fclose($handle);
});
$response->headers->set('Content-Type', 'application/force-download');
$response->headers->set('Content-Disposition', 'attachment; filename="bookings.csv"');
return $response;

据我所见,没有任何代码在流式传输时在内存中积累数据。我使用Symfony 2.7、Doctrine MongoDB ODM 1.0.3和PHP 5.5.9,内存限制为256M。

如注释中所述,问题与游标结果上的不可idratable对象有关。为了捕获异常并写入正确的数据,您可以捕获异常。为了捕获异常,需要重写循环。例如,您可以按如下方式执行do-while循环:

do {
        try 
        {
            // this could rise an exception
            $result = $cursor->getNext()
            // Transform the Booking object to a flat array.
            $data = $this->constructRow($result);
            // Print CSV header
            if (!$headerPrinted) {
                fputcsv($handle, array_keys($data), ';', '"');
                $headerPrinted = true;
            }
            // Add a line in the csv file.
            fputcsv($handle, $data, ';', '"');
        } catch ('Exception $e) 
        {
          // do nothing and process next element
        } 
 }
while($cursor->hasNext())

我还建议使用一个库来管理CSV写作,例如:

  • Goodby CSV
  • 联盟CSV

希望这能帮助

听起来这与php.ini 中的内存限制有关

ini_set('memory_limit','16M');

把它放大,看看你得到的行数