0

I would like to load my own sensitive file from a service with Symfony. I'm a little confused with all the stuff found in the Symfony documentation. By default, I can access only on file placed in my /web folder.

But if I'm right, this folder is public. So i want to move my file to /src/AppBundle/Resources/myfile.txt

Here are several links I've read without finding anything clear.

Should I use the __DIR__ statement?

I've 2 goals to achieve if possible:

  • Access a file with a relative path to the project (e.g AppBundle or 'www' folder)
  • Access files in /random/place/on/my/server without exposing the entire the path.

One last thing to add; There is no way to copy my files in the web folder, there is more than 10Gb of files to access, so no cache, no copy, just a simple acces.

Community
  • 1
  • 1
Apex
  • 393
  • 1
  • 6
  • 17

1 Answers1

11

You can serve a file in Symfony using the following code:

    $response = new BinaryFileResponse($filepath);

    // Set headers
    $response->headers->set('Cache-Control', 'private');
    $response->headers->set('Content-Type', $file->getMimeType());
    $response->headers->set('Content-Disposition', $response->headers->makeDisposition(
        ResponseHeaderBag::DISPOSITION_ATTACHMENT,
        $file->getName()
    ));

    return $response;

Make sure you also have:

use Symfony\Component\HttpFoundation\ResponseHeaderBag;
use Symfony\Component\HttpFoundation\BinaryFileResponse;

How to get $file is explained in the documentation of the Finder component you've linked. When you put the code in a controller in AppBundle you can use:

$filepath = __DIR__ . '/../Resources/myfile.txt';

Or you can use a path relative to your application root:

$rootDir = $this->get('kernel')->getRootDir();
$filepath = $rootDir. '/src/AppBundle/Resources/myfile.txt';

$filepath could be an absolute path as well, but make sure that the webserver can read from that path. As long as the you use a path outside the webservers docroot (which is probably <your app root>/web), you can control acces to the file using voters and other custom logic in PHP.

Getting the $file object:

In order to construct your fileobject you might use the Finder component, but this is actually nothing more that a wrapper around SPL filehandling. You could therefore also use:

$file = new \SplFileInfo($filepath);

Why BinaryFileResponse?

When sending large files, PHP might read the entire file into memory before sending it as a response to the client. This might cause PHP to run out of memory. Using BinaryFileResponse avoids this by reading the file block-by-block and streaming those blocks to the client. Therefore, never send big files using a Response object.

Using X-Sendfile:

While using the code above prevents PHP from running out of memory, it still keeps PHP busy while sending the file. When you have a busy webserver you might want to keep as many PHP processes as possible free to render webpages, not streaming files.

Luckely, instead of this:

Request ----> Webserver ----> PHP-FPM ----> File
Response <--- Webserver <---- PHP-FPM <---- File

You can do this:

Request ----> Webserver ----> PHP-FPM
Response <--- Webserver <---- PHP-FPM
                ^  |
                |  |
                |  V
                File

This is done by using X-Sendfile.

That is wicked, how can I use that?

Getting X-Sendfile to work requires two simple steps:

  1. Adding a header to your response that tells your webserver that it should server a file.
  2. Configuring your webserver to read that header and actually server that file.

The name of the header and how your webserver is configured depends on the type of webserver you are using:

Note that Nginx calls it X-Accel. The header you pass to Nginx is X-Accel-Redirect but that is the only difference.

Community
  • 1
  • 1
Sander Toonen
  • 3,463
  • 35
  • 54