Skip to content
[shareaholic app="share_buttons" id="14657658"]
Share this..

Serving up Files from a database in CakePHP

2008 July 27
by Eddie

This article is mostly in response to Daniel Hofstetter’s great article, File Upload with CakePHP which explains how to save uploaded files in their own database table. In his example (I assume for the sake of space and time in the article) he serves the files right from his controller.

This is not true to the MVC format however, so I wanted to provide an alternative for those who were unsure how to separate the two.

For those of you who ask, why store and serve files (pdfs, images, etc) from a database
, and not just save it do a directory. Well I can’t speak for everyone, buts here is my reasoning;

  • Security

    Only users with proper credentials are allowed to view the file. No direct url to ‘bypass.’

  • Integrity

    Files associated with posts or users can be automatically deleted when that post or user is.

  • Maintenance

    I can deploy an update to my server and overwrite the existing structure without losing a single meta file.

Below is the code for the model, the controller, view and layout. I also included the recommended way to make the calls from your existing views.



Start by adding the code below into your controller, I created a fresh controller dedicated to project files. This allows users to add pdfs and other documents to scholar research projects.

    function download($id) {
       //I like to restrict this to logged in users
            $this->Session->setFlash("Ya'lls gots'ta be logged in fer to fetch these pages");
       //IMPORTANT!  turn off debug output, will corrupt filestream.      
        Configure::write('debug', 0);
        $file = $this->ProjectFile->findById($id,'user_id = '.$user['User']['id']);
       //just in case its been deleted, or someone is getting frisky
            $this->Session->setFlash("Problem. Either;<ul><li>We no longer have that file</li><li>We never did.</li><li>You don't have rights</li></ul>");
        //set the file variabl up for use in our view
        // we'll use a new layout, file, that will allow custom headers
    function show($id) {
      //set up a variable, so the view well knwo to show it, not prompt to download
//in my actual controller i do some logic here to set up an array of ''allowed file ids''  but to kepp it simple, well assume everyone can see
       //IMPORTANT!  turn off debug output, will corrupt filestream.      
        Configure::write('debug', 0);
        $file = $this->ProjectFile->findById($id,'user_id = '.$user['User']['id']);
        if(!isset($file['ProjectFile']['name']) || substr($file['ProjectFile']['type'],0,5)!='image'){
            echo 'Not an image file';
        //set the file variabl up for use in our view
        // we'll use our new layout, file,BUT well also use the same view, download



The model is pretty straight-forward, and just declares the relationships.

class ProjectFile extends AppModel {
    var $name = 'ProjectFile';
    	//The Associations below have been created with all possible keys, those that are not needed can be removed
	var $belongsTo = array(
			'User' => array('className' => 'User',
								'foreignKey' => 'user_id',
								'conditions' => '',
								'fields' => '',
								'order' => ''
			'Project' => array('className' => 'Project',
								'foreignKey' => 'project_id',
								'conditions' => '',
								'fields' => '',
								'order' => ''

Related or Parent Model



To enforce referential integrity, i.e. have files deleted when parents are deleted, than add the following to your ‘parent’ model.

	//The Associations below have been created with all possible keys, those that are not needed can be removed
	var $hasMany = array(
			'ProjectFile' => array('className' => 'ProjectFile',
								'foreignKey' => 'project_id',
								'dependent' => true,
								'conditions' => '',
								'fields' => '',
								'order' => '',
								'limit' => '',
								'offset' => '',
								'exclusive' => '',
								'finderQuery' => '',
								'counterQuery' => ''



Next we create a special layout that will tell your browser the stream is a file, not html.

header('Content-type: ' . $file['ProjectFile']['type']);
if(!isset($inpage)) header('Content-Disposition: attachment; filename="'.$file['ProjectFile']['name'].'"');
echo $content_for_layout;

Remember the view above serves both download and show actions in the controller. The ‘Content-Disposition’ is what will cause the browser to treat it like a download or load it in page. The variable ‘$inpage’ is used by our controller and layout to know whether to prompt for download or not.



Finally we create the actual view to push the file stream out to our user.

print $file['ProjectFile']['data'];

Example: Displaying files in pages

Now you can use the show method to allow users to see images without downloading them. You can leverage this to show images within your posts or pages in the same manner

/app/views/projects/view.ctp (example page to show images)

This code would go in my projects views, for you it might be the user profile page or an article.

echo $html->image(array('controller'=>'project_files','action'=>'show',$project['ProjectFile']['id']),array('title'=>'This is a related file to a project'));
echo '<img src="/project_files/show/174" title="This is a related file to a project" />';

Note: This trick can also be used to create download-able xml and other text based files like code snippets. This makes it easy for users to sve the file rather than having to copy and paste.


19 Responses leave one →
  1. December 18, 2008

    Thanks, you have just saved me a few hours of work!!! I completed the same task using php a few months back and now I’m looking to port the site to Cake and this is exactly what I needed, great work many thanks.

  2. Eddie permalink*
    December 18, 2008

    Thanks, I am glad this worked for you. Also that is a very nice site you have, happy ventures.

  3. March 16, 2009

    Hi Eddie,

    Thanks for the code, though unfortunately I am receiving an “Array to string conversion” in my view for each image I attempt to display. Has anyone else mentioned this to you? Note I am using CakePHP 1.1.

    I am able to display the file by using this code:

    <img src="../images/">

    The only problem is that this causes the CakePHP session to reset (security level is set to HIGH) if multiple files are shown in the view. When I change the security level to MEDIUM, everything works perfectly, but I’d like to keep it at HIGH.

  4. Eddie permalink*
    March 16, 2009

    I assume your referring to this portion of code;

    echo $html->image(array('controller'=>'project_files','action'=>'show',$project['ProjectFile']['id']),array('title'=>'This is a related file to a project'));

    I believe the ability to use arrays for URLs came in 1.2, instead try;

    echo $html->image('/project_files/show/'.$project['ProjectFile']['id'],array('title'=>'This is a related file to a project'));
  5. March 17, 2009

    Hi Eddie,

    Thanks for the update. It shows the images now but unfortunately my Cake security session issue still persists. I had hoped that loading images the correct “Cake way” would help with this.

  6. Stuart permalink
    May 13, 2009

    Great article. Saved me hours of slogging through the cookbook. I’m having some difficulty serving up .pdf files though. When I click on a link in my view to show() a .pdf file, I get the “you have chosen to open…” dialog with the $file[‘ProjectFile’][‘id’] value where I would expect the $file[‘ProjectFile’][‘name’] to be. I understand why this is happening but I don’t see how it could work this way. If I go ahead and click “OK” on the dialog I get a corrupted document message from acrobat, which I suspect is because it’s not getting the $file[‘ProjectFile’][‘data’]. I’ve removed the logged-in check and the image type condition and obvious model/controller names but otherwise your code is unchaged. Any ideas on .pdf files?

    Thanks again!

  7. Eddie permalink*
    May 13, 2009

    Glad this got this you started.
    To be honest I designed the show function with images in mind and the download function for things like ppts, and pdfs. I haven’t attempted showing PDFs inline.

    Let me know if you figure it out.

  8. October 9, 2009


    I’m having a little problem using your script to serve zip files. The file downloads perfectly, but if I try to unzip it, I get an error.

    I opened the zip file with a text editor and noticed there is a blank line being added at the top of the file. If I remove this line and save the file, it unzips perfectly. Also, if I put the file in a web-accessible directory and navigate directly to the zip file, it works perfectly.

    There are no echos or prints in my download action. I’ve tried everything I can think of.

    Any suggestions?

  9. Eddie permalink*
    October 9, 2009

    I would check the php files for blank lines just before or after < ?php ?> tags. For example


    As these may be printed and inserted above the cells actually content.

    (On that note you try running a dump directly from the cell to be sure your not saving a blank line when uploading.

  10. October 11, 2009

    Hey, problem solved!

    thanks for the help. Turns out one of my helpers was adding a blank line at the top of my page. I switched the downloads to another controller and viola! Works like a charm.

    One quick question though, is there anyway to redirect to a certain page after the download has been initiated?

    Thanks for the wonderful component!

  11. Milos permalink
    May 21, 2010

    Unfortunately this isn’t fix my problem. I don’t understand how this can work because you didn’t set Content-type. where do you use $inpage?

  12. June 16, 2010

    How to save the image in database and to
    retrieve the image from database and to display in the page. i’ve
    tried that but i cant able to get the proper output. could you pls
    help me to do this.

  13. leo permalink
    June 23, 2010

    Eddie – my browser (Firefox 3.6.3) is masking the layout code. Here it is if anybody else is having problems (original is surrounded by php tags):

    header(‘Content-type: ‘ . $file[‘ProjectFile’][‘type’]);
    if(!isset($inpage)) header(‘Content-Disposition: attachment; filename=”‘.$file[‘ProjectFile’][‘name’].'”‘);
    echo $content_for_layout;

  14. Eddie permalink*
    June 24, 2010


    Thanks! I recently updated my syntax highlighter and it seemed to break several code snippets above.

    I have made the fix so everyone can see the right controller, view and model code.


  15. xav0989 permalink
    July 22, 2010

    How about the media view?

  16. November 15, 2010

    Thanks, this is a great little piece of code, and very well explained!

  17. Sandeepan Kundu permalink
    October 30, 2013

    Well it works for most file but when the mime type is
    or other formats of excel or word, it fails.

Trackbacks and Pingbacks

  1. CakePHP : signets remarquables du 23/02/2009 au 26/02/2009 | Cherry on the...
  2. Serving Files from a database with CakePHP | Edward A. Webb (.com) | Source code bank

Leave a Reply

Note: You can use basic XHTML in your comments. Your email address will never be published.

Subscribe to this comment feed via RSS