18

I would like to know how can i return an image from the controller without any template. I would like to use it for pixel tracking in a newsletter.

I start with this code

    $image = "1px.png";
    $file =    readfile("/path/to/my/image/1px.png");
    $headers = array(
        'Content-Type'     => 'image/png',
        'Content-Disposition' => 'inline; filename="'.$file.'"');
    return new Response($image, 200, $headers);

But on the navigator i have a broken link (file not found...)

Arnaud B
  • 183
  • 1
  • 1
  • 4
  • Just seen the message but this would help. [Downloading a file with symfony](http://www.inanzzz.com/index.php/post/cko4/downloading-a-file-with-symfony) – BentCoder Nov 25 '15 at 14:51

6 Answers6

35

According to the Symfony Docs when serving files you could use a BinaryFileResponse:

use Symfony\Component\HttpFoundation\BinaryFileResponse;
$file = 'path/to/file.txt';
$response = new BinaryFileResponse($file);
// you can modify headers here, before returning
return $response;

This BinaryFileResponse automatically handles some HTTP request headers and spares you from using readfile() or other file functions.

franbenz
  • 696
  • 1
  • 10
  • 16
11

Right now you return the filename as response body and write the file-content to the filename property of Content-Disposition.

return new Response($image, 200, $headers);

should be:

return new Response($file, 200, $headers);

... and ...

'Content-Disposition' => 'inline; filename="'.$file.'"');

should be ...

'Content-Disposition' => 'inline; filename="'.$image.'"');

right?

Further take a look at this question.

Community
  • 1
  • 1
Nicolai Fröhlich
  • 51,330
  • 11
  • 126
  • 130
  • Perfect ! the solution was the other topic. In your case, i had an 500 error code. thank you for the link – Arnaud B Jul 01 '13 at 20:41
6

This works fine for me.

$filepath = "/path/to/my/image/chart.png";
$filename = "chart.png";

$response = new Response();
$disposition = $response->headers->makeDisposition(ResponseHeaderBag::DISPOSITION_INLINE, $filename);
$response->headers->set('Content-Disposition', $disposition);
$response->headers->set('Content-Type', 'image/png');
$response->setContent(file_get_contents($filepath));

return $response;
Francesco
  • 405
  • 3
  • 15
4

file_get_contents is a bad idea. Reading a large list of images via file_get_contents killed my little server. I had to find another solution and this works now perfect and very fast for me.

The key is to use readfile($sFileName) instead of file_get_contents. The Symfony Stream Response is able to take a callback function which will be executed while sending ($oResponse->send()). So this is a good place to use readfile().

As a little benefit I wrote down a way of caching.

use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\StreamedResponse;

class ImageController
{

    public function indexAction(Request $oRequest, Response $oResponse)
    {
        // Get the filename from the request 
        // e.g. $oRequest->get("imagename")
        $sFileName = "/images_directory/demoimage.jpg";
        if( ! is_file($sFileName)){
            $oResponse->setStatusCode(404);

            return $oResponse;
        }

        // Caching...
        $sLastModified = filemtime($sFileName);
        $sEtag = md5_file($sFileName);

        $sFileSize = filesize($sFileName);
        $aInfo = getimagesize($sFileName);

        if(in_array($sEtag, $oRequest->getETags()) || $oRequest->headers->get('If-Modified-Since') === gmdate("D, d M Y H:i:s", $sLastModified)." GMT" ){
            $oResponse->headers->set("Content-Type", $aInfo['mime']);
            $oResponse->headers->set("Last-Modified", gmdate("D, d M Y H:i:s", $sLastModified)." GMT");
            $oResponse->setETag($sEtag);
            $oResponse->setPublic();
            $oResponse->setStatusCode(304);

            return $oResponse;
        }

        $oStreamResponse = new StreamedResponse();
        $oStreamResponse->headers->set("Content-Type", $aInfo['mime']);
        $oStreamResponse->headers->set("Content-Length", $sFileSize);
        $oStreamResponse->headers->set("ETag", $sEtag);
        $oStreamResponse->headers->set("Last-Modified", gmdate("D, d M Y H:i:s", $sLastModified)." GMT");

        $oStreamResponse->setCallback(function() use ($sFileName) {
            readfile($sFileName);
        });

        return $oStreamResponse;
    }
}
Phil
  • 206
  • 3
  • 4
3

Quick anwser

use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\ResponseHeaderBag;

class AnotherController extends Controller {

  public function imagesAction($img = 'my_pixel_tracking.png'){

    $filepath = '/path/to/images/'.$img;

      if(file_exists($filepath)){
        $response = new Response();
        $disposition = $response->headers->makeDisposition(ResponseHeaderBag::DISPOSITION_INLINE, $img);
        $response->headers->set('Content-Disposition', $disposition);
        $response->headers->set('Content-Type', 'image/png');
        $response->setContent(file_get_contents($filepath));
        return $response;
      }
      else{
        return $this->redirect($this->generateUrl('my_url_to_site_index'));
      }
  }
}
Nolwennig
  • 1,613
  • 24
  • 29
2

Also, I had to change:

$file =    readfile("/path/to/my/image/1px.png");

to this:

$file =    file_get_contents("/path/to/my/image/1px.png");

Seemed like readfile was echoing contents as it read it forcing headers to output early and negating the forced Content-Type header.