【问题标题】:How to force download a .csv file in Symfony 2, using Response object?如何使用响应对象在 Symfony 2 中强制下载 .csv 文件?
【发布时间】:2012-04-24 22:42:21
【问题描述】:

我正在使用 Symfony 2 制作一个“下载”控制器,其唯一目的是发送标头以便我可以强制下载 .csv 文件,但它无法正常工作。

$response = new Response();
$response->headers->set('Content-Type', "text/csv");
$response->headers->set('Content-Disposition', 'attachment; filename="'.$fileName.'"');
$response->headers->set('Pragma', "no-cache");
$response->headers->set('Expires', "0");
$response->headers->set('Content-Transfer-Encoding', "binary");
$response->headers->set('Content-Length', filesize($fileName));
$response->prepare();
$response->sendHeaders();
$response->setContent(readfile($fileName));
$response->sendContent();

$fileName 是一个"info.csv" 字符串。这就是我在控制器中的操作,没有返回语句。当我尝试返回 Response 对象时,文件的内容显示在浏览器中,而不是我想要的结果。

我发现的问题是,在某些页面中,我确实收到了我的info.csv file,但在其他页面中,我得到的只是一条消息:

No webpage was found for the web address: http://mywebpage.com/download Error 6 (net::ERR_FILE_NOT_FOUND): The file or directory could not be found.

我完全确定该文件存在,所以肯定还有其他问题。此外,routing.yml 工作正常,因为我确实从也链接到该路径的其他页面获取文件。 Apache 错误日志没有显示任何关于它的信息。

以前有人在 Symfony 2 上强制下载 .csv 文件吗?如果是这样,我做错了什么?

【问题讨论】:

    标签: csv symfony download


    【解决方案1】:

    这是一个在生产中运行良好的最小示例:

    class MyController
    public function myAction()
    
        $response = $this->render('ZaysoAreaBundle:Admin:Team/list.csv.php',$tplData);
    
        $response->headers->set('Content-Type', 'text/csv');
        $response->headers->set('Content-Disposition', 'attachment; filename="teams.csv"');
    
        return $response;
    

    如果你愿意,你可以用新的 response 和 response->setContent 替换渲染调用。

    您对控制器内部没有返回语句的评论令人费解。控制器返回响应。让框架负责将内容发送到浏览器。

    【讨论】:

    • $tpldata 未定义
    【解决方案2】:

    我意识到这篇文章有点老了,奇怪的是,除了 stackoverflow 上的这篇文章之外,几乎没有关于如何在 symfony 2 中进行 CSV 导出的好的资源。

    无论如何,我将上面的示例用于客户竞赛网站,并且效果很好。但是今天我收到了一封电子邮件,在我自己测试之后,代码已经损坏 - 我能够使用少量结果进行下载,但是数据库现在导出了超过 31,000 行,它要么简单地显示文本,要么使用chrome,基本上什么都没做。

    对于任何有大量数据导出问题的人,这是我设法开始工作的,基本上是按照 Cerad 建议的替代方式:

        $filename = "export_".date("Y_m_d_His").".csv";
        $response = $this->render('AppDefaultBundle:Default:csvfile.html.twig', array('data' => $data));
    
        $response->setStatusCode(200);
        $response->headers->set('Content-Type', 'text/csv');
        $response->headers->set('Content-Description', 'Submissions Export');
        $response->headers->set('Content-Disposition', 'attachment; filename='.$filename);
        $response->headers->set('Content-Transfer-Encoding', 'binary');
        $response->headers->set('Pragma', 'no-cache');
        $response->headers->set('Expires', '0');
    
        $response->prepare();
        $response->sendHeaders();
        $response->sendContent();
    

    编辑:经过更多测试并提高允许的最大秒数后,我意识到之前的代码正在打印顶部的标题,因此我更新了代码。

    【讨论】:

    • 谢谢。我必须通过 $response->prepare() 方法传递 Request $request 对象才能让它工作。
    【解决方案3】:

    这对我来说可以导出 CSV 和 JSON。

    Twig 文件命名为:export.csv.twig、export.json.twig

    控制器:

    class PrototypeController extends Controller {
    
    public function exportAction(Request $request) {
    
        $data = array("data" => "test");
    
        $format = $request->getRequestFormat();
    
        if ($format == "csv") {
            $response = $this->render('PrototypeBundle:Prototype:export.' . $format . '.twig', array('data' => $data));
            $filename = "export_".date("Y_m_d_His").".csv";
            $response->headers->set('Content-Type', 'text/csv');
            $response->headers->set('Content-Disposition', 'attachment; filename='.$filename);
    
            return $response;
        } else if ($format == "json") {
            return new Response(json_encode($data));
        }
    }
    }
    

    路由:

    prototype_export:
        pattern:  /export/{_format}
        defaults: { _controller: PrototypeBundle:Prototype:export, _format: json }
        requirements:
            _format:  csv|json
    

    树枝:

    export.csv.twig(在这里做逗号分隔的事情,这只是一个测试)

    {% for row in data %}
    {{ row }}
    {% endfor %} 
    

    export.json.twig(数据发送json_encoded,这个文件是空的)

    希望这会有所帮助!

    【讨论】:

      【解决方案4】:

      这就是我设法让 Silex 返回 csv 的方法:

      // $headers in an array of strings
      // $results are the records returned by a PDO query
       $stream = function() use ($headers, $results) {
          $output = fopen('php://output', 'w');
          fputcsv($output, $headers);
          foreach ($results as $rec)
          {
              fputcsv($output, $rec);
          }
      
          fclose($output);
      };
      
      return $app->stream($stream, 200, array(
          'Content-Type' => 'text/csv',
          'Content-Description' => 'File Transfer',
          'Content-Disposition' => 'attachment; filename="test.csv"',
          'Expires' => '0',
          'Cache-Control' => 'must-revalidate',
          'Pragma' => 'public',
      ));
      

      您可能还需要使用 Javascript 做一些 Jiggery Poker(我正在通过 AJAX 下载),但我只需要这个 post 来让它工作。

      【讨论】:

        【解决方案5】:

        您可以在每种情况下使用简单的功能来导出 csv 以供下载...

        public function getResponse(array $data, $filename, $headers = array())
        {
            if(substr(strtolower($filename), -4) == '.csv') {
                $filename = substr($filename, 0, -4);
            }
        
            $tmpFile = $this
                ->_getContainer()
                ->get('kernel')
                ->getRootDir()
                . '/../var/tmp_'.substr(md5(time()),0,5);
        
            if(file_exists($tmpFile)) unlink($tmpFile);
        
            $handle = fopen($tmpFile, 'w');
        
            foreach ($data as $i => $row) {
        
                $row = (array) $row;
        
                if($i == 0) fputcsv($handle, array_keys($row));
        
                fputcsv($handle, $row);
        
            }
        
            fclose($handle);
        
            $Response = new Response(file_get_contents($tmpFile));
        
            unlink($tmpFile);
        
            $filename = preg_replace('[^a-z0-9A-Z_]', '', $filename);
        
            $headers = array_merge([
                'Expires' => 'Tue, 01 Jul 1970 06:00:00 GMT',
                'Cache-Control' => 'max-age=0, no-cache, must-revalidate, proxy-revalidate',
                'Content-Disposition' => 'attachment; filename='.$filename.'.csv',
                'Content-Type' => 'text/csv',
                'Content-Transfer-Encoding' => 'binary',
            ], $headers);
        
            foreach ($headers as $key => $val) {
                $Response->headers->set($key, $val);
            }
        
            return $Response;
        }
        

        【讨论】:

          【解决方案6】:

          使用 Sonata 的 Exporter 怎么样:

          use Exporter\Writer\CsvWriter;
          /**
           * @param array $orders
           */
          public function exportToCsv($orders)
          {
              $rootdir = $this->get('kernel')->getRootDir();
              $filename = $rootdir . '/data/orders.csv';
              unlink($filename);
              $csvExport = new CsvWriter($filename);
              $csvExport->open();
              foreach ($orders as $order)
              {
                  $csvExport->write($order);
              }
              $csvExport->close();
              return;
          
          }
          

          如果文件已经存在,它会崩溃,因此 unlink-command。

          【讨论】:

          • 这如何帮助 OP 通过浏览器下载 CSV?无论如何,在尝试取消链接之前尝试测试文件是否存在......
          猜你喜欢
          • 1970-01-01
          • 2018-07-26
          • 2011-01-18
          • 1970-01-01
          • 2013-06-20
          • 1970-01-01
          • 2020-02-19
          • 1970-01-01
          • 2016-03-05
          相关资源
          最近更新 更多