A lot of web applications offer to download some kind of PHP file. Maybe you want to implement some kind of export feature or you have paid content in form of a PDF or other media or you just want to secure your PDF files in the same way as the rest of your application (i.e. username/password). If this is the case you wouldn’t place your PDF-files in a subfolder of the web-Directory of your Symfony project, because all these file can be accessed directly via browser.
So how to serve your PDF-Files through a Symfony controller? it is an easy one. Just assume you put your pdf-files in a directory called “media” on the root-level of your Symfony-project.
root
|-- apps
|-- cache
|-- config
| |-- demo.pdf
|-- media
|-- lib
|-- web
These files cannot be reached by Browser, so we need a new routing information for Symfony to get them accessible via the webbrowser. For that open the routing.yml of your application and add the following rule topmost in this file.
mediafile:
url: /media/:filename.pdf/*
param: { module: media, action: show }
From now on if you try to access a PDF-File in the media-folder (i.e. http://domain/media/demo.pdf) the media-Module with the action show is called. That was easy but how to implement this action. First you need to create the media module with the symfony command line tool:
$> symfony generate:module appname media
Afterwards add the following method to the class in the media/actions/actions.class.php file:
public function executeShow(sfWebRequest $request) {
// being sure no other content wil be output
$this->setLayout(false);
sfConfig::set('sf_web_debug', false);
$pdfpath = sfConfig::get('sf_root_dir').DIRECTORY_SEPARATOR.'media'
.DIRECTORY_SEPARATOR
.$request->getParameter('filename').'.pdf';
// check if the file exists
$this->forward404Unless(file_exists($pdfpath));
// Adding the file to the Response object
$this->getResponse()->clearHttpHeaders();
$this->getResponse()->setHttpHeader('Pragma: public', true);
$this->getResponse()->setContentType('application/pdf');
$this->getResponse()->sendHttpHeaders();
$this->getResponse()->setContent(readfile($pdfpath));
return sfView::NONE;
}
Thats it! You can improve this Action method a little bit. For example you can add a “download”-switch. If this switch is set to 1 the file is downloaded instead of shown in the browser (i.e. http://domain/media/demo.pdf/download/1).
public function executeShow(sfWebRequest $request) {
// being sure no other content wil be output
$this->setLayout(false);
sfConfig::set('sf_web_debug', false);
$pdfpath = sfConfig::get('sf_root_dir').DIRECTORY_SEPARATOR.'media'
.DIRECTORY_SEPARATOR
.$request->getParameter('filename').'.pdf';
// check if the file exists
$this->forward404Unless(file_exists($pdfpath));
// Adding the file to the Response object
$this->getResponse()->clearHttpHeaders();
$this->getResponse()->setHttpHeader('Pragma: public', true);
$this->getResponse()->setContentType('application/pdf');
// download-switch
if ($request->getParameter('download', 0) == 1) {
$this->getResponse()->setHttpHeader('Content-Disposition',
'attachment; filename="'.
$request->getParameter('filename').'"');
}
$this->getResponse()->sendHttpHeaders();
$this->getResponse()->setContent(readfile($pdfpath));
return sfView::NONE;
}
As we now use a Symfony-Controller you can do just use the complete Symfony infrastructure. You like to secure the PDF-files? Just enable the Symfony security mechanisms (is_secure: on in the security.yml) and only logged in users can download them. In the same way you can create paid content. Or you can add watermarks to your PDF using Zend_Pdf for example.
Hope this is helpful for you.