Downloading of a dynamically generated file from a Symfony controller

Published on 2021-01-10 • Modified on 2021-01-10

In a previous snippet, we saw how to force the dowload of a file that already exists on the file system. But, generally, you want to provide dynamic content like for a CSV export. In this one, we will see how to do with using a temporary system file, so we don't have to generate a file stored in the public directory; that would be a security issue.


<?php

declare(strict_types=1);

// src/Controller/Snippet/Snippet129Trait.php

namespace App\Controller\Snippet;

use Symfony\Component\Filesystem\Filesystem;
use Symfony\Component\HttpFoundation\BinaryFileResponse;
use Symfony\Component\HttpKernel\KernelInterface;

/**
 * I am using a PHP trait to isolate each snippet in a file.
 * This code should be called from a Symfony controller extending AbstractController (as of Symfony 4.2)
 * or Symfony\Bundle\FrameworkBundle\Controller\Controller (Symfony <= 4.1).
 * Services are injected in the main controller constructor.
 *
 * @property KernelInterface $kernel
 */
trait Snippet129Trait
{
    public function snippet129(): BinaryFileResponse
    {
        $tmpFileName = (new Filesystem())->tempnam(sys_get_temp_dir(), 'sb_');
        $tmpFile = fopen($tmpFileName, 'wb+');
        if (!\is_resource($tmpFile)) {
            throw new \RuntimeException('Unable to create a temporary file.');
        }

        $data = [
            ['name', 'firstname', 'age'],
            ['COil', 'Doo', random_int(30, 42)],
            ['Fab', 'Pot', random_int(30, 42)],
            ['Glas', 'Dun', random_int(30, 42)],
        ];
        foreach ($data as $line) {
            fputcsv($tmpFile, $line, ';');
        }
        $response = $this->file($tmpFileName, 'dynamic-csv-file.csv');
        $response->headers->set('Content-type', 'application/csv');

        fclose($tmpFile);

        return $response; // That's it! 😁
    }
}

 Run this snippet  More on Stackoverflow  Random snippet