File upload with CakePHP

Published on and tagged with cakephp  programming

In this post I will describe how to upload and store a file in the database. Yes, you read correct, I will describe how to store a file in the database. I know, a lot of people do not recommend storing files in a database due to performance reasons. Of course, this is an important point you have to consider when you design your application. The reasons why I store files often in the database are:

  • all data are stored in one place: the database
  • it is easier to test
  • it is easier to develop as I do not have to keep database and file system in sync

Enough of bla-bla, let us dive in the code. First the table definition for MySQL:

CREATE TABLE my_files (
  id INT(11) NOT NULL AUTO_INCREMENT,
  name VARCHAR(75) NOT NULL,
  type VARCHAR(255) NOT NULL,
  size INT(11) NOT NULL,
  data MEDIUMBLOB NOT NULL,
  created DATETIME,
  modified DATETIME,
  PRIMARY KEY (id)
);

Note: Use MEDIUMBLOB or LONGBLOB as data type unless you know for sure that the file size of the uploaded files is never bigger than 64kB.

The model is straight-forward:

// app/models/my_file.php
class MyFile extends AppModel {
    var $name = 'MyFile';
}

We omit the controller for the moment and create directly the view:

// app/views/my_files/add.ctp  (Cake 1.2)
<?php 
    echo $form->create('MyFile', array('action' => 'add', 'type' => 'file'));
    echo $form->file('File');
    echo $form->submit('Upload');
    echo $form->end();
?>

// app/views/my_files/add.thtml (Cake 1.1)
<form action="/my_files/add" enctype="multipart/form-data" method="post">
    <?php echo $html->file('File'); ?>
    <?php echo $html->submit('Upload'); ?>
</form>

So, now we are ready to build the controller and to implement the add() function:

// app/controllers/my_files_controller.php (Cake 1.2)
class MyFilesController extends AppController {
    function add() {			
        if (!empty($this->data) && 
             is_uploaded_file($this->data['MyFile']['File']['tmp_name'])) {
            $fileData = fread(fopen($this->data['MyFile']['File']['tmp_name'], "r"), 
                                     $this->data['MyFile']['File']['size']);
            
            $this->data['MyFile']['name'] = $this->data['MyFile']['File']['name'];
            $this->data['MyFile']['type'] = $this->data['MyFile']['File']['type'];
            $this->data['MyFile']['size'] = $this->data['MyFile']['File']['size'];
            $this->data['MyFile']['data'] = $fileData;
					
            $this->MyFile->save($this->data);

            $this->redirect('somecontroller/someaction');
        }
    }
}

// app/controllers/my_files_controller.php (Cake 1.1)
class MyFilesController extends AppController {
    function add() {			
        if (!empty($this->params['form']) && 
             is_uploaded_file($this->params['form']['File']['tmp_name'])) {
            $fileData = fread(fopen($this->params['form']['File']['tmp_name'], "r"), 
                                     $this->params['form']['File']['size']);
            $this->params['form']['File']['data'] = $fileData;
					
            $this->MyFile->save($this->params['form']['File']);

            $this->redirect('somecontroller/someaction');
        }
    }
}

Easy, isn’t it? Up to now we have stored the file in the database. To retrieve the file from the database, we need a download() action which we add to our controller:

function download($id) {
    Configure::write('debug', 0);
    $file = $this->MyFile->findById($id);
		
    header('Content-type: ' . $file['MyFile']['type']);
    header('Content-length: ' . $file['MyFile']['size']); // some people reported problems with this line (see the comments), commenting out this line helped in those cases
    header('Content-Disposition: attachment; filename="'.$file['MyFile']['name'].'"');
    echo $file['MyFile']['data'];
			
    exit();
}

Well, I know that this action is probably not very cake-like, the proper way would be to use a layout and a view, but this way I have less to write ;-)

So, that’s it. We have finished our very simple upload/download application.

Update (2006-08-05): Fixed a security hole in the code above, see also “Be careful with file uploads”. Thanks to Lamby.
Update (2007-06-09): Fixed a small bug in the download function. Thanks to Wilhelm Raab!
Update (2007-10-15): The name “File” led to problems with the core class with the same name, hence I renamed table name, model name, etc. Thanks to “fyi” for the hint!
Update (2007-12-11): Updated for CakePHP 1.2.
Update (2009-02-20): Adding comment to the header(‘Content-length’) statement in the download method

167 comments baked

  • Re: Newbie file upload question | DEEP in PHP

    […] Re: Newbie file upload question Posted by News On February 1st, 2011 / No Comments Hi there This should be helpful: [link] […]

  • SitePunk

    Thanks for the tutorial!

    @H-man: I had the whitespace on the start of the output as well. I checked my custom App_Controller.php which had an empty space on it’s end. (after the })

  • cakebaker

    @SitePunk: You are welcome :)

  • kavitha rajendran

    Hi,
    After modifying selenium-api.js file, i have created selenium-server.jar file by following command.
    jar -cvf selenium-sevrer.jar selenium-server

    But when I am trying to start selenium server, I am getting following error.
    D:\krajendr\Desktop\debug>java -jar seleniumserver.jar
    Failed to load Main-Class manifest attribute from
    seleniumserver.jar

    D:\krajendr\Desktop\debug>
    please let me know the solution, if anyone knows.

  • taqman

    hi I try to validate file type is doesn’t work
    var $validate = array(
    ‘type’ => array(

    ‘checktypeedit’ =>array(
    ‘rule’ => array(‘checkType’,false),
    ‘message’ => ‘Invalid File type’,

    )

    )
    );
    function checkType($data, $required = false){
    $data = array_shift($data);
    if(!$required && $data[‘error’] == 4){
    return true;
    }
    $allowedMime = array(‘application/x-bittorrent’);
    if(!in_array($data[‘type’], $allowedMime)){
    return false;
    }
    return true;
    }

  • taqman
    var $validate = array(
                'type' => array(
                   
                    'checktypeedit' =>array(
                        'rule' => array('checkType',false),
                        'message' => 'Invalid File type',
    
                    )
                   
                    )
    );
        function checkType($data, $required = false){
            $data = array_shift($data);
            if(!$required && $data['error'] == 4){
                return true;
            }
            $allowedMime = array('application/x-bittorrent');
            if(!in_array($data['type'], $allowedMime)){
                return false;
            }
            return true;
        }
    }

    it’ doesn’t work what is the right way

  • cakebaker

    @taqman: Hm, is the checkType() method called? If the method is not called, then the $data array you pass to the save() method probably has the wrong structure. As you can see in the add() method in the article, I have to convert the form data to the structure expected by CakePHP’s save() method.

    Hope this helps!

  • taqman

    @cakebaker thank for answer but your code is useful but the UI
    doesn’t friendly I try to apply to
    http://pixelcone.com/tutorial/ajax-file-upload-using-jquery-and-cakephp-media-plugin/
    I don’t want media plugin and try to insert you code separate in controller this code http://bin.cakephp.org/view/1830824100
    but when I upload always failed although match in validate
    can you help me to fix it

  • cakebaker

    @taqman: As I mentioned in my previous comment, I would first check whether your checkType() method gets called. If it is called, I would then check the content of the $data variable, especially the call of array_shift() looks a bit suspicious to me.

    Hope this brings you one step further.

  • taqman

    hi again cakebaker
    I have apply your download function

    function download($id) {
        Configure::write('debug', 0);
        if($id != null){
        $file = $this->Script->findByid($id);
    
        header('Content-type: ' . $file['Attachment']['file_content_type']);
        header('Content-length: ' . $file['Attachment']['file_size']); // some people reported problems with this line (see the comments), commenting out this line helped in those cases
        header('Content-Disposition: attachment; filename="'.$file['Attachment']['file_name'].'"');
        echo $file['Attachment']['file_object'];
         $this->Session->setFlash('Download Complete');
        exit ();
         $this->redirect(array($this->referer()));
        }else{
         $this->Session->setFlash('File not Found');  
         $this->redirect(array('action'=>'script_index'));
        }
    }

    I have question how to fix it to work and I need counter download
    thank

  • cakebaker

    @taqman: Hm, what doesn’t work with the snippet you posted?

    To count the downloads you have to use something like:

    $this->MyFile->updateAll(array('MyFile.download_counter'=>'MyFile.download_counter+1'), array('MyFile.id'=>40));
    

    Hope this helps!

  • Aneeq

    The code to upload file in PHP is very simple, but we need to understand the flow which is a below.

    1. Browse the file from a local system
    2. Upload to server
    3. Server keeps it on a temporary path
    5. Copy from temporary to permanent path

    Create a file upload form

    Filename:

    Note:

    * An enctype attribute of the tag has been specified.
    * This attribute specifies which content-type to use when submitting the form
    * We have used “multipart/form-data” to upload binary data, like the contents of a file, to be uploaded
    * If proper enctype is not provided, upload will not work.
    * File upload is a huge security risk so you must check what type of files are being uploade

    Create a file upload script (upload-file.php)

    This will upload the file to the specified path.

    Note:

    * The default file upload size using a browser is usually 2MB so files larger than this size may not upload. You will have to alter the file upload setting on the server.
    * You need to set write permission to the folder where file needs to be upload.
    * In our case, the “uploads” folder needs to have a 777 permission on a linux/unix server.

    Source:

    http://phphelp.co/2012/05/18/how-to-upload-a-file-in-php/

  • cakebaker

    @Aneeq: Thanks for your comment!

  • How to upload a file and attach it to email using cakephp 2.x … « CakePHP Articles

    […] http://cakebaker.42dh.com/2006/04/15/file-upload-with-cakephp/ Above one use database, I don’t wanna use database to store files. And it use cakephp 1.x which cannot run in 2.x. […]

  • victor

    please i need a code that enable me upload and output image(like passport photograph) from Database to the browser i have stalled up 2 project because of this

  • supervago

    Good afternoon, I’m newbie with cakephp and the version I have is 1.3, you have the code for that version? thanks

  • Brew

    I used this in a cake 2.5.5 project. I had problems using download function for images. I got an extra character in the beginning of the body. It had nothing to do with the addslashes suggestions, but it had to do with flushing the output buffer. i used this to solve the problem:

    function download($id) {
    		if (headers_sent()) throw new Exception('Headers sent.');
    			while (ob_get_level() && ob_end_clean());
    				if (ob_get_level()) throw new Exception('Buffering is still active.');
    
    		$filedl = $this->myfile->findById($id);
    		$this->response->body($filedl['myfile']['data']);
    		$this->response->download($filedl['myfile']['name']);
    		$this->response->type($filedl['myfile']['type']);
    		$this->response->header(array('Content-length' => $filedl['myfile']['size']));
    		return $this->response;
    	}
  • amil baba

    amil baba

    […]we like to honor many other online web pages around the internet, even when they aren’t linked to us, by linking to them. Beneath are some webpages worth checking out[…]

  • Cheap Banners

    Cheap Banners

    […]the time to read or stop by the subject material or web-sites we have linked to beneath the[…]

  • home improvement ideas

    home improvement ideas

    […]one of our visitors just lately encouraged the following website[…]

  • skin beauty

    skin beauty

    […]Every after in a while we decide on blogs that we read. Listed below are the newest websites that we select […]

  • satta matka

    satta matka

    […]usually posts some very interesting stuff like this. If you are new to this site[…]

  • SATTA MATKA RESULT

    SATTA MATKA RESULT

    […]Sites of interest we have a link to[…]

  • read this post here

    read this post here

    […]please go to the web pages we stick to, such as this one, as it represents our picks from the web[…]

  • Lubitski

    Lubitski

    […]always a major fan of linking to bloggers that I enjoy but don’t get a lot of link enjoy from[…]

Bake a comment




(for code please use <code>...</code> [no escaping necessary])

© daniel hofstetter. Licensed under a Creative Commons License