How to send a file as response from a controller in Symfony 3

How to send a file as response from a controller in Symfony 3

Return a file (any type of file) as a response from a controller, is a regular task that can be easily achieved. To serve a static file in a Symfony controller, we recommend you to use the BinaryFileResponse class. This class represents an HTTP response delivering a file (it extends the Response class).

A. Return file in Browser

You can return a file to be viewed in the browser (when supported) returning the BinaryFileResponse class directly as it doesn't have a custom content disposition:

<?php

namespace myBundle\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\Controller;

// Import the BinaryFileResponse
use Symfony\Component\HttpFoundation\BinaryFileResponse;

class DefaultController extends Controller
{
    public function indexAction()
    {
        // You only need to provide the path to your static file
        // $filepath = 'path/to/TextFile.txt';

        // i.e Sending a file from the resources folder in /web
        // in this example, the TextFile.txt needs to exist in the server
        $publicResourcesFolderPath = $this->get('kernel')->getRootDir() . '/../web/public-resources/';
        $filename = "TextFile.txt";

        // This should return the file located in /mySymfonyProject/web/public-resources/TextFile.txt
        // to being viewed in the Browser
        return new BinaryFileResponse($publicResourcesFolderPath.$filename);
    }
}

You can use the previous snippet too if you're sure that the returned file won't be viewed in the browser and instead will be downloaded. However, if you need to download the file, we recommend you to force the mimetype of the file and set the content disposition as an attachment as shown in the following example.

B. Return file as a download

Sometimes, you need to return a file to be downloaded in your controller, therefore you need to use the BinaryFileResponse class, but you need to modify the disposition of the response and add the mimetype of the file that will be downloaded in the headers. This is perfect for files like PDFs, document files, images, and video that you want your customers to download rather than read online:

<?php

namespace myBundle\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\Controller;

// Import required classes
use Symfony\Component\HttpFoundation\BinaryFileResponse;
use Symfony\Component\HttpFoundation\ResponseHeaderBag;
use Symfony\Component\HttpFoundation\File\MimeType\FileinfoMimeTypeGuesser;

class DefaultController extends Controller
{
    public function indexAction()
    {
        // You only need to provide the path to your static file
        // $filepath = 'path/to/TextFile.txt';

        // i.e Sending a file from the resources folder in /web
        // in this example, the TextFile.txt needs to exist in the server
        $publicResourcesFolderPath = $this->get('kernel')->getRootDir() . '/../web/public-resources/';
        $filename = "TextFile.txt";

        // This should return the file to the browser as response
        $response = new BinaryFileResponse($publicResourcesFolderPath.$filename);

        // To generate a file download, you need the mimetype of the file
        $mimeTypeGuesser = new FileinfoMimeTypeGuesser();

        // Set the mimetype with the guesser or manually
        if($mimeTypeGuesser->isSupported()){
            // Guess the mimetype of the file according to the extension of the file
            $response->headers->set('Content-Type', $mimeTypeGuesser->guess($publicResourcesFolderPath.$filename));
        }else{
            // Set the mimetype of the file manually, in this case for a text file is text/plain
            $response->headers->set('Content-Type', 'text/plain');
        }

        // Set content disposition inline of the file
        $response->setContentDisposition(
            ResponseHeaderBag::DISPOSITION_ATTACHMENT,
            $filename
        );

        return $response;
    }
}

As you can see, in the following example we guessed the mimetype of the file to download using the FileinfoMimeTypeGuesser class in case it is supported, otherwise the mime type needs to be provided manually by you.

The BinaryFileResponse will automatically handle Range and If-Range headers from the request. It also supports X-Sendfile (see for Nginx and Apache). The delivery of a static file which depends on an application header is known as the X-Sendfile feature. To make use of it, you need to determine whether or not the X-Sendfile-Type header should be trusted and call trustXSendfileTypeHeader()if it should using:

BinaryFileResponse::trustXSendfileTypeHeader();

Return a dinamically created file

When sending a dinamically created file, you must add a Content-Disposition header to your response. In a regular HTTP response, the Content-Disposition response header is a header indicating if the content is expected to be displayed inline in the browser, that is, as a Web page or as part of a Web page, or as an attachment in this case, that is downloaded and saved locally. In this case, our file doesn't exist in the storage of the server but in the memory and variables.

While creating this header for basic file downloads is easy, using non-ASCII filenames is more involving. The makeDisposition() abstracts the hard work behind a simple API:

<?php

namespace myBundle\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\Controller;

// Include the Response and ResponseHeaderBag
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\ResponseHeaderBag;

class DefaultController extends Controller
{
    public function indexAction()
    {
        // Provide a name for your file with extension
        $filename = 'TextFile.txt';
        
        // The dinamically created content of the file
        $fileContent = "Hello, this is the content of my File";
        
        // Return a response with a specific content
        $response = new Response($fileContent);

        // Create the disposition of the file
        $disposition = $response->headers->makeDisposition(
            ResponseHeaderBag::DISPOSITION_ATTACHMENT,
            $filename
        );

        // Set the content disposition
        $response->headers->set('Content-Disposition', $disposition);

        // Dispatch request
        return $response;
    }
}

The execution of the indexAction of the previous controller, should generate automatically a download for a text file with the name TextFile and extension .txt in your browser. If you want to view the file directly in the Browser, you can change the ResponseHeaderBag::DISPOSITION_ATTACHMENT to ResponseHeaderBag::DISPOSITION_INLINE.

Symfony 3.2

With the release of Symfony 3.2, there's a new method on the controllers namely file that forces the download of a file in the browser:

use Symfony\Component\HttpFoundation\File\File;
use Symfony\Component\HttpFoundation\ResponseHeaderBag;

public function fileAction()
{
    // load the file from the filesystem
    $file = new File('/path/to/some_file.pdf');

    return $this->file($file);

    // rename the downloaded file
    return $this->file($file, 'custom_name.pdf');

    // display the file contents in the browser instead of downloading it
    return $this->file('invoice_3241.pdf', 'my_invoice.pdf', ResponseHeaderBag::DISPOSITION_INLINE);
}

Have fun !

Become a more social person