Generating a CSV file from an action to bring it to the users of one of our websites I encountered myself with the problem of Symfony’s MVC model and decorator pattern. Although I was using echo statements directly, Symfony was waiting for the action to finish to send headers and allow the download, so the memory load was too high and the process was broken too frequently.

These days you know it’s not a question of whether the client is simply working with a dial up; everyone has broadband now. The solution for me was to specify raw headers directly inside the action before the echo statements and return sfView::HEADER_ONLY as result of the action.

In development mode it generates a “Headers already sent” error, but in production environment PHP omits it. You can use PHP’s error_reporting instruction set to 1 to omit it also in development environment.

Let’s see an example:

public function executeMyAction()
{
  ...
  header('Content-Type: application/msexcel;charset=ISO-8859-1');
  header('Content-Disposition: attachment;filename='.$name);
  ...
  // echo statements
  ...
  error_reporting(1);
  return sfView::HEADER_ONLY;
}

3 Comments on “Progressive download in Symfony”

You can track this conversation through its atom feed.

  1. Enrique says:

    Have you tried something like Listing 11-29 in http://www.symfony-project.org/book/1_1/11-Ajax-Integration ?

    The example is for ajax, but I think it should work for everything.

  2. carlos says:

    Yes, but if you have to send a large amount of data (not only a JSON string) the process spend so much memory and breaks (large CSV files…). If you write strings directly you use less memory.

  3. jzarate says:

    Rather than echoing the output straight from within the action, a classical symfony action/template pair could be the better approach.
    The action scheme is thought for data calculation and database consulting, and it’s , by nature, buffered. Nothing is output until the executeXXX() method reachs the sfView::XXX line.
    That’s why you need to use the echo function, because that’s the only way you can get output from the action before reaching the end of the method.
    However, the action, naturally, complains.

    It’s better to try to do this at the template. Pass an iterator variable (or some other thing that lets you iterate over a big object without keeping it in memory, a resultset by instance), then there is a completely different scenario.
    Take a csv file that you want to parse. If you try to store the whole csv file within a string, your system will probably explode, but you can make some object with a next() method, that lets you iterate through it…
    Once you’re in the template, standard php is at command, and standard php is streamed by default.
    My suggestion is then:
    In the action:

    public function executeMyAction()
    {
    $this->getResponse()->setContentType('application/msexcel;charset=ISO-8859-1');
    $this->getResponse()->setHttpHeader('Content-Disposition',"attachment;filename=codigos.csv");
    $this->csv = $csv;
    return sfView::SUCCESS;
    }

    An then the template:

    < ?php
    echo $csv->getHeaderString();
    while($row = $csv->next(true))
    {
    echo $row;
    }

Leave a Reply

XHTML: You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>