-1

I'm trying to send a generated CSV file using Symfony 5 and somehow seem to have reached a limit I can't overcome. For testing reasons I have broken it down to the basic of the basics; so I send static data, generate a txt-file and do no logic whatsoever. In my example code below, if I do 65 iterations the file is generated and sent to the browser as a download (that's 4KB). If I do 66 or add a single letter to the $data sample data string it's all displayed in the browser window. The exact same code works perfectly well outside Symfony even with 1000 iterations. I also don't get an error or anything out of the dev.log that could help.

public function saveCSV(Request $request, DelcampeParser $parser) {
    $response = new Response();

// same with
//  $response = new StreamedResponse();
//  $response->setCallback(function(){});

    $response->headers->set('Content-Type', 'text/plain; charset=utf-8');
    $disposition = HeaderUtils::makeDisposition(
        HeaderUtils::DISPOSITION_ATTACHMENT,
        'foo.txt'
    );
    $response->headers->set('Content-Disposition', $disposition);

    $data = array(0 => 'abcdefghijklmnopqrstuvwxyz');
    for ($a=1;$a<=66;$a++) {
        var_dump($data);
    }

    return $response;
}

What's wrong? Why am I not able to generate a bigger file in Symfony?

Thank you.

[edit] I have a new theory: PHP's output buffering is set to 4KB. Whenever the buffer is full, buffered data is sent to the Browser. That unfortunately happens before the headers have been sent by Symfony, thus generating a browser output. How can I change that behavior?

fun2life
  • 139
  • 1
  • 11
  • 1
    var_dump writes in standard output. Use StreamedResponse .... https://symfony.com/doc/current/components/http_foundation.html#streaming-a-response – SilvioQ Jan 20 '20 at 16:38
  • it's not about that. 4095 characters generate a download, 4096 generate a browser output. No matter how I generate this characters – fun2life Jan 20 '20 at 16:58
  • mmm ... What Web server are you using? Some output is sent to browser before headers? Check this example ... https://gitlab.com/snippets/1931727 ... they works like charm. – SilvioQ Jan 20 '20 at 17:27
  • Your script seems promising. Thanks. But at the moment I also need the the header data sent before generating huge amount of data. One question though: how do I get variable data into the callback function? Where you have the $data=array();... I need to access a Service that generates the actual data I want in my csv and to pass on user data from a form to this service. At the moment I only get a Symfony error that tells me neither $parser (holdig the typehinted service), nor $postdata (holding the data submitted via the form) is defined. – fun2life Jan 21 '20 at 11:52

1 Answers1

0

I now have an answer with StreamedResponse that works and hopefully also fulfills all requirements and recommendations by Symfony:

// in the Controller
public function saveCSV(Request $request, Parser $parser) {
    $response = new StreamedResponse();

    $data = $request->get("sd");    // sd comes out of a form that posts a multidimensional array
    $response->setCallback(function() use ($data, $parser) {
        $parser->generateCSV($data);
    });

    $response->headers->set('Content-Type', 'text/csv; charset=utf-8');
    $disposition = HeaderUtils::makeDisposition(
        HeaderUtils::DISPOSITION_ATTACHMENT,
        "delcampeimport_".date("Ymd-Hi").".csv",
    );
    $response->headers->set('Content-Disposition', $disposition);

    return $response;
}

and my Service

public function generateCSV($dataarray) {
    $out = fopen('php://output', 'w+');

    // send header rows of csv file
    fputs($out, "website_visibility,category_id,title, /* ..., */ images\r\n");

    // generate data
    foreach($dataarray AS $stamp) {
        $stampdata = array ( 
            'a' => 'CH',
            'b' => $stamp['category'],
            'c' => htmlentities($stamp['title']),
            // ...
            'aa' => $stamp['img1'].
            ($stamp['img2'] ? ";".$stamp['img2']:"").
            ($stamp['img3'] ? ";".$stamp['img3']:"")
        );

        // send csv data
        fputcsv($out, $stampdata);
    }
    fclose($out);
}

Thanks for the inputs

fun2life
  • 139
  • 1
  • 11