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

166 comments baked

  • auto

    Pretty interesting! It seems like you are saying it’s best for development and testing. Do you have a method to convert back once that is done? And would you recommend something like this in a live app, like a way to keep people from leeching files by linking directly to them?

  • cakebaker

    No, I don’t do any conversions, i.e. in the live application are the files stored in the database, too. Otherwise it would be useless to test a functionality you do not use in the live application ;-)

  • scott lewis

    A View is there to define the presentation of the data to the user. With a raw bytestream such as a file, there is no ‘presentation’ to be defined…

  • Lex Slaghuis

    I am having some difficulties retrieving binairies correctly ( the image gets jibbered or the pdf is corrupt). Text files work perfectly.

    I use the exact code as provided! First i open the add page in the browser and upload the file. Then I open the http://……/files/download/1 page manually. (instead of 1 a possible id number of the file)

    It doesn’t work on both IE and firefox. My server is a WAMP, latest version on a windows 2003 server.

    I suspect the issues to depend on corrupt decoding through the http protocol.

  • Lex Slaghuis

    issue resolved;

    bug in the add function!
    insert addslashes method for direct database insertion….
    $fileData = addslashes(fread(fopen($this->params['form']['File']['tmp_name'], “r”),
    $this->params['form']['File']['size']));

  • cakebaker

    @Lex Slaghuis: I am glad you could resolve the issue. I think you have to use the “addSlashes” function if you use different encodings. In my application I use UTF-8 everywhere, and I don’t experienced any problems.

  • Shane Shepherd

    Let’s say I wanted to add the field product_id to the files table as a foreign key to the products table. How would this look in function add() {}?

  • cakebaker

    @Shane Shepherd: If the product_id is in the same form as the file upload is you do not have to add anything. Otherwise you have to do something like:

    $this->params['form']['File']['product_id'] = $productId;

  • Shane Shepherd

    @cakebaker – Sweet! You rock! Thanks!

  • Lamby

    Hmm. What would you think would happen if uploaded a form of my own something along the lines of:

    Assuming there was a mechanism for obtaining uploaded files, I would then have your mySQL password, something very dangerous on a shared host. Enjoy :)

  • Lamby

    [input hidden="data[File][tmp_name]” value=”/var/www/cake/app/config/database.php” /]
    [input hidden="data[File][size]” value=”9999999″ /]

    (It ate my previous post’s HTML tags, so reposting with square brackets. Sorry)

  • robdeman

    I had trouble with corrupted JPEGs. I turned out that the view file that displays the image upload form was using a layout file with a ‘wrong’ Byte Order Marker, it was set to ‘dos’. Most text editor can change this to ‘unix’. This resolved my corrupted images.

    used on: WinXP, WAMP server, Firefox

  • MawciKurL

    I’m a newbie in CakePHP. I want to upload Excel files but not to store it in the database, I just want to read them. Can I do this without creating a model? And can a model created without a database table pair?

  • cakebaker

    @MawciKurL: You do not need a model for file uploads.

    I am not sure if I understand your second question correctly. But if your question was “is it possible to create a model which doesn’t use a table”, than the answer is yes. You have to add the following snippet to your model:
    var $useTable = false;

  • Lamby

    Just to elaborate on my previous two comments, you are leaving a large security hole open if you do not check the filenames with file_is_uploaded() or similar. Regards, Lamby

  • cakebaker

    @Lamby: I have fixed it in the post.

  • JP

    Great article, just what I am looking for, but I’m having a problem when I try to implement this in my project (complete newbie, sorry!).
    I’ve copied into my project and the view add.thtml loads fine, I select a file and press upload but I get an object not found error “The requested URL was not found on this server.”, but I know it is there because it refers to itself! Am I missing something very obvious?
    Cheers, JP

  • cakebaker

    @JP: Hm, maybe you have to change the action property in your add.thtml file?

  • elswidi

    thanks cakebaker for this helpful example. I want to use it as a milestone and build on top of it – ie. adding more inputs in “add.thtml” !?
    I am facing problems trying to add an extra text input as such :
    input(‘Modelname/fieldname’, array(‘size’ => ’50′))?>

    I believe the problem is because the following 2 lines:
    $this->params['form']['Site']['data']=$siteData;
    $this->Site->save($this->params['form']);

    the data that is to be saved is only about the image !!

    any idea on how to modify the add() fuinction in the controller so that it accepts extra params from the form in add.thtml??

  • elswidi

    I know that the question might sounds silly but i am really a newbie :)
    Anyway… I solved it with in very odd way. I discovered that I can retrieve post data from my form in my controler in from 2 Arrays. First is params['form'] which is used in the example. Second is params['data']. That was the key of my solution since ‘data’ – which is the image- is overwritten by $this->params[’form’][’Site’][’data’]=$siteData;
    So, I decided to do the following: use params['data'] to save extra inputs and params['forms'] to save the image.

    here is exactly what I did:
    //app/controllers/files_controller.php
    function add()
    {
    //first save all form data except the image using params['data']
    if (empty($this->params['data']))
    {
    $this->render();
    }
    else
    {
    if ($this->Site->save($this->data['Site']))
    {
    $this->redirect(‘sites/index’);
    }
    }

    // socnd save the image using params['forms']
    // same code in the example
    if (!empty($this->params['form']) && is_uploaded_file($this->params['form']['Site']['tmp_name']))
    {
    $siteData = fread(fopen($this->params['form']['Site']['tmp_name'],”r”),$this->params['form']['Site']['size']);
    $this->params['form']['Site']['data']=$siteData;
    $this->Site->save($this->params['form']);
    $this->Site->save($siteData);
    $this->redirect(‘sites/index’);
    }

    }

    hope might be useful but still not convinced with it !!?

    Any ideas??

  • cakebaker

    @elswidi: Hm, I don’t know why you use two saves:
    $this->Site->save($this->params[’form’]);
    $this->Site->save($siteData);

    With something like:
    $this->params['form']['Site']‘otherdata’] = $this->data['Site']['otherdata'];
    $this->params[’form’][’Site’][’data’]=$siteData;
    $this->Site->save($this->params['form']['Site'];

    it should be possible to save all your data with just one save instead of three saves.

  • warren

    Hi im followed your instructions here on this post. and all worked well with my localserver. however, when im downloading the image from my site on a remote server, the image went crappy. im not sure where the problem lies.

    if the problem is with my code, then it shouldnt work in my localserver in the firstplace. i doubt if its cakephp’s code because otherwise you would encounter the problem too.

    thanks

  • cakebaker

    For all users that encounter the same problem as warren, the solution can be found in http://groups.google.com/group/cake-php/browse_thread/thread/bf5a3c382b69d599/8f1b0453ffd13640

  • EmheMah

    It took me a while to realize that the line added for the security problem had ’ instead of ‘

    is_uploaded_file($this->params[’form’][’File’][’tmp_name’]))

    should be

    is_uploaded_file($this->params['form']['File']['tmp_name']))

  • cakebaker

    @EmheMah: Thanks for the hint, I have fixed it. Sorry for the inconvenience.

  • Tom

    I’m confused why people like to upload files to their database. My thinking is it slows down database access time when there are multiple users and it unnecessarily increases the size of your database. Many low cost web hosts out there that offer shared hosting put a restriction on your database size. Fortunately more and more (and many of the intelligent ones) make your database size as large as your account plan’s disk space size so it doesn’t matter. BUT like I said, I feel it slows things down….because rather than accessing files through a simple HTTP request, it has to go through some PHP and then access the database. Of course the answer is security. No one can straight up access your database. Yes, we can htaccess things and protect directories too though.

    Anyway, the way I approached file uploads was by automatically generating folders based upon a numbering system…so if it’s post “15″ then the image(s) will go in a directory like, “images/15/” or whatever files.

    This would then avoid the entire possibility of being able to see the php file contents. While one may be able to upload the php file (given that they knew the path), they could download or view it all they wanted…it’s in a normal directory on a web server…the data won’t be visible.

    I say eliminate problems through simplicity. Of course storing files/images in a directory makes things infinitely easier for other image gallery scripts, etc.

  • cakebaker

    @Tom: Thanks for your comment.

    Well, it depends on your project whether it makes sense to store files in the database. And yes, it is slower than accessing the files directly. But unless you develop a high-traffic web application that’s usually not a problem.

    Yes, I am for eliminating problems through simplicity. In my case it is by storing files in the database, which allows me to avoid permission or synchronization problems ;-)

  • cakebaker » Be careful with file uploads

    [...] In a comment lamby pointed out a possible security hole in the code of my post “File upload with CakePHP”. He is right, if you know the location of app/config/database.php you can retrieve the database settings including the password. [...]

  • Bjarke Sørensen

    I’m going to build a (hopefully) high traffic site and choose to store images in DB as described above.

    The advantage is the possibility of storing privileges and data in one place.

    But to prevent the possible issues that speed might be I’m going to implement some caching is the download function and I will add a thumbnail cache too.

    That should get the best from both worlds.

    Another cool feature is that you can easily prevent deep-linking to files (photos) on your site – if that’s what you want.

  • cakebaker

    @Bjarke Sørensen: Thanks for your comment and good luck with your application!

  • Peeter

    It’s sure easy but not practical at all (as you mention). Sometimes I think (while reading different cakephp articles) that those develeopers haven’t had any sites or apps with more than 1000 concurrent users or they are just plain rich to throw money on infrastructures (servers, ram etc).

    why not let the webserver deliver the static content? That is just the thing what it does best and if you need an ACL you can allways make somekind of passthrough script.

    Think of 100 users pressing the link for the 10meg file at the same time and you get a whopping 100x10meg transfer rate from your mysql pipe exhausting the availible bandwith at full speed and only freeing the memory while the transactions are all over and only putting other requests at hold. IMHO it’s not reasonable if compaired just passing the file identifier to apache as URL or to the passthrough script for delivering the file directly from hdd

  • cakebaker

    @Peeter: Well, as I said before, the decision whether to store files in the database depends on the application you develop. There are a lot of sites out there which will never have 1000 concurrent users, and so performance isn’t an issue for them. On the other side there are high-traffic sites where performance is very important and where having files in the database could become a bottleneck. The analysis of the requirements should give you the answer with which sort of application you have to deal, and based on that you can decide.

  • nateklaiber

    I think this method, mixed with some caching, could be an optimal solution….

    Thanks for the info.

  • jpsykes » From March 9th to March 12th I looked at…

    [...] cakebaker » File upload with CakePHP [...]

  • Marcos

    Does anybody know why I can upload only small files (

  • cakebaker

    @Marcos: Did you check the value of upload_max_filesize in your php.ini?

  • pabs

    I’m a newbie..

    I wanted to upload and image but not in the database but in a folder in the webroot, could you help me on this? No resizing needed.

    thanks.

  • cakebaker

    @pabs: move_uploaded_file (http://php.net/move_uploaded_file) should help you to accomplish what you want.

  • Wilhelm Raab

    Great short tutorial cakebaker (although I was searching for something ’bout CakePHP’s file handling functions, so I guess I’ll just have to keep looking).

    However, I found an error in Your code. Very simple one, the HTTP header Content-Disposition should have the filename enclosed in “: header(‘Content-Disposition: attachment; filename=”‘.$file['File']['name'] .’”‘);

    Cheers, // W. Raab

  • cakebaker

    @Wilhelm: Thanks for pointing me to that bug. I fixed it in the article.

  • Bahari

    Do you know how to use FileHandlerComponent in CakePHP? I already used it, but i didn’t work.

    I wrote this in my view :

    url(“/customers/upload”) ?> method=”post”>

    and this function in my customers controller :

    var $components = array(‘FileHandler’);

    function upload()
    {
    $this->FileHandler->upload(‘userfile’,'files’);
    }

    but it didn’t work. Can you help me to fix this problem?
    Thanks before..

  • cakebaker

    @Bahari: I am sorry, but I don’t know the FileHandlerComponent. But I am sure the author of this component can help you.

  • Antonie Potgieter

    I don’t like the way you put the file data directly into the database. I’d personally use move_uploaded_file() and save the file to a new directory from the temp directory. Wouldn’t this make the application so much faster?

  • cakebaker

    @Antonie: Well, as always in programming, every solution has its advantages and disadvantages. It depends on your concrete application whether it makes sense to store the files in the database. E.g. if an upload functionality is used rather seldom, then performance is usually not that important.

  • Sadjow Medeiros Leão

    I like resize image jpg file. with lib gd.

    I would only like guard the archives in a folder and the names of them in the data base. making an image of the normal size and another one being miniature.

    thx.

  • Nikhil

    PLs. tell how can we upload a file with a size greater that 2 MB in cakephp which is the default size my php.ini file handles

  • cakebaker

    @Sadjow: Maybe the following article helps you: http://www.bogdan-net.com/archives/24

    @Nikhil: Well, if you define 2MB as maximal size for uploaded files in the php.ini you cannot upload files with a greater size with CakePHP. If you want to upload with a greater size you have to change the value in the php.ini.

  • H-man

    I’m using this script, and it works like a charm.. almost. When I try to download a file, a whitespace is added at the start of the file before the last echo is called, which makes the file unreadable (if it’s a binary file).

    Outputting the data before the headers are defined shows that the header functions are the sinners. If anyone has experienced the same error, I would be glad to hear from you.

  • cakebaker

    @H-man: Hm, I cannot reproduce this issue here. Do you get the same effect with text files?

  • confuzus

    Great article, as always.

    One trick I figured out in collusion with another developer is to calculate a hash (salt+md5 your file contents), and save the resulting hash in the table.

    This allows you to check the hash if you want to make sure you only store one copy of the same file, or to check and see if a file actually has been updated by comparing pre-upload hash and current hash.

    This ALSO prevents the exploit that lamby discusses and keeps your files from being sequentially read, ie none of this -

    http://site.com/files/1
    http://site.com/files/2
    http://site.com/files/3

    - because you can set it up to look for the hash instead of the id.

    ~confuzus

  • cakebaker

    @confuzus: Thanks for this trick, it may be really useful in certain situations.

  • Kums

    Hello,

    Thanks for the code.I used the code snippet and the data was saved to the database.This is what i did. I uploaded a JPG file and then tried to download the file. When i tried to download it asked for Save to Disk or Open. I saved it and tried to open and it said it Cannot Open. I need to know how to do two things

    1. Why this error is coming? I am able to save and download text files properly

    2. I donot want this file dialog to open instead render the uploaded image directly in my view as image tag

    Thanks

  • cakebaker

    @Kums: 1. Hm, sounds like an encoding problem to me. Does it work if you use some special characters like äöü in your text files?

    2. Simply add the url to the controller action to your image tag:

    <img src="/controller/action">
    

    Hope that helps :)

  • Zunayed

    How do I validate a file type?

  • cakebaker

    @Zunayed: You could define a validation rule for the “type” in your File model.

    Hope that helps :)

  • Jamie Carruthers

    thanks for this tutorial, it was very helpful. The download function works fine, but how do I display this image in a page?

  • cakebaker

    @Jamie: You could define a similar function as the download function without setting the Content-Disposition header. And in your view where you want to display the image you have to use something like (you could also use the Html helper for this purpose):

    <img src="/yourcontroller/displayimageaction/12" />
    

    Hope that helps :)

  • fyi

    Just a FYI for anybody using Cake 1.2 (maybe 1.1?) the model File is reserved, so doing any $this->File operations will result in odd behavior. Change all the references to File and Files to something else like SaveFile or StoreFile

  • cakebaker

    @fyi: Thanks for the hint, I modified the article accordingly.

  • Tim Daldini

    Anybody could elaborate on confuzus’ idea about md5 hashing? I don’t understand how this prevents the exploit lamby was talking about.

    Thanks

  • cakebaker

    @Tim: Hm, I don’t see how confuzus’ idea would prevent the exploit. What it prevents is that you no longer can download any file by guessing the respective ID.

  • kodiew

    Im getting,

    Warning (512): Method HtmlHelper::file does not exist.

    Using Cake 1.2

    Any help?

  • kodiew

    Warning (512): Method HtmlHelper::file does not exist

    Warning (512): Method HtmlHelper::submit does not exist

    Cake 1.2. Why do i get these messages?

  • cakebaker

    @kodiew: Those methods have been moved to the FormHelper in Cake 1.2. So you have to add the FormHelper to the $helpers array of your controller, and to use $form instead of $html. But I am not sure whether you have to modify other things in this example to make it work with Cake 1.2. I have to test it myself.

  • Helmut

    @kodiew:

    That’s because there were some changes with version 1.2.
    http://api.cakephp.org/1.2/class_form_helper.html#a4df5df8e975a5b2b1f54fbc1eeb846b

    Just change
    “$html->file” to “form->file”
    and
    “$html->submit” to “form->end(‘Upload file’)”
    in the file “app/views/images/upload.ctp
    (Also note the ending .ctp instead of .thtml is new in version 1.2, but .thtml still works)

    Then add
    var $helpers = array(‘Form’);
    to the file app/controllers/images_controller.php

    HTH.

    Other than that, I am also interested in how to avoid the security issues that lamby talked about. (I am using 10 – 40 MB files.)

    Helmut

  • cakebaker

    @Helmut: Thanks for your comment!

    Well, the security issue Lamby talked about should be fixed in the example.

  • kodiewix

    Hi, thanks a lot for those. That got rid of the errors. I got this working fine with 1.1. No luck in 1.2 yet!! the form displays and i add a file, when i click upload it just hangs for a second then seems to just refresh the page. Also what will the view/download file be like?

  • kodiew

    all good! Got it all working sweet! Thanks everyone!!

  • cakebaker

    @kodiew: Cool to hear that :) What was the problem?

  • mary

    I also use the 1.2 pre_beta version and if i try to upload the file i am getting the error as

    Invalid argument supplied for foreach() [CORE\cake\dispatcher.php, line 381]

    Shall anybody help me to come out from this issue.

  • Adam

    I am also using the pre_beta (5875) and am getting the exact same error as Mary.

  • Adam

    @mary: have a look at your view, the above form tags no longer seem to work with the pre_beta.

    instead use something like this:
    create(‘User’,array(‘action’=>’saveUsers’, ‘type’=>’file’));?>

  • Adam

    create('User',array('action'=&gt;'saveUsers', 'type'=&gt;'file'));?&gt;

  • cakebaker

    @mary, Adam: I adapted the code in the article to make it work with CakePHP 1.2.

  • fmic

    Can I get the Cake 1.1.* version, as opposed to the 1.2 version?

  • cakebaker

    @fmic: The article covers both versions.

  • A few things about CakePHP, PostgreSQL and Xapian « Dokeos lead developer’s Weblog

    [...] File upload with CakePHP (although not exactly what I’m looking for) [...]

  • ben

    many thanks for this short and precise introduction.
    one question remains though: are the fields ‘type’ and ‘size’ in the file array checked and set by cake or do they just get passed through as in ‘regular’ php? because if these were the ‘regular’ fields i could not depend on their value or even existence as these get set by the client’s browser (or not) afaik
    i’m afraid they’re the ‘regular’ ones but as you explicitly mentioned them in your v1.2 controller i got a spark of hope that cake might once again have saved me precious time …

  • cakebaker

    @ben: Yes, they are the “regular” fields set by the client’s browser. There is no Cake magic involved in this case.

  • sbeam

    I was using basically the same method, and it worked for weeks but then suddenly I was getting corrupted JPEGs and TIFFs. After a a frustrating hour it turns out \x0a was being prepended to the files when downloaded – a UNIX newline. This was brought in with a 3rd-party file which ended in ?> and then a newline for good measure.

    Moral of the story – watch for output before you echo the binary stuff here – and in general – DON’T put ?> at the end of a .php file when there is nothing else to follow. It serves no purpose and can only cause headaches for others.
    just had to get that off my chest :)

    Also – I’ve found that MSIE doesn’t always pay attention to the filetype and Content-disposition correctly, especially when docs like PDFs or MSWord are loaded inside of it. The apparent file extension in the URL can be important. You might want to make sure the download link looks something like this in the view you are linking from:
    /documents/fetch//

    so it looks like /documents/fetch/1290/somefile.pdf
    thanks for the article.

  • cakebaker

    @sbeam: Thanks for your comment! I think having the file extension in the URL could also be useful for the user, so that he knows what type of file he downloads.

  • yunhaihuang

    hi!i want to upload and store an image in app/webroot/img/uploads by cakephp,but “move_uploaded_file” doesn’t work. cakephp show “failed to open stream:No such file or directory…”;
    my code is bellow:
    function admin_add(){
    if(is_uploaded_file($this->data['Edition']['cover']['tmp_name'])){
    $destination = ‘/app/webroot/img/uploads/’.$this->data['Edition']['cover']['name'];
    move_uploaded_file($this->data['Edition']['cover']['tmp_name'],$destination);
    }

    }

    please tell me why and how to overcome this bug; thanks a lots!

  • cakebaker

    @yunhaihuang: Are you sure the path the $destination variable contains is correct? Shouldn’t it be something like “/path_to_my_project/app/webroot/…”?

  • yunhaihuang

    the $destination variable is right. up to now, i haven’t fixed the bug yet. i need your help!
    my purpose is upload an image and store it into /app/webroot/img/uploads.
    i need for some detail solution.

  • cakebaker

    @yunhaihuang: Hm, does the following work?

    if (is_uploaded_file($this->data[’Edition’][’cover’][’tmp_name’])){
        $fileData = fread(fopen($this->data['Edition']['cover']['tmp_name'], "r"),
                                         $this->data['Edition']['cover']['size']);
        echo $fileData;
    }
    
  • yunhaihuang

    thanks for your help! i’ve solved the problem. it’s my fault, the $destination isn’t correct as i thought. thanks lots for your help and apologize for my mistakes.

  • cakebaker

    @yunhaihuang: I’m glad to hear you could solve the problem. Bake on!

  • Paul

    I wonder how to enable a flash mp3 player to play audio files uploaded this style since adobe flash only supports files.

    Ideas anyone?

  • cakebaker

    @Paul: Hm, I don’t know flash, but if it doesn’t allow you to import files from an url, then you probably have bad luck and you have to store the files on the file system.

  • Alfredo Herrejon

    Hi, thanks for the help, i think its very easy you explain all very good, an d the best you did it for both cakephp 1.1 and 1.2, but i have a question, what’s up with the name of the files, is there is one with the same name of another you want to upload?, i read all the comments, a friend ask you this before, but what you said: I have fixed it in the post. i don’t understand that, can you explain me a little more please, thanks

  • cakebaker

    @Alfredo: Thanks for your comment!

    In the first version I didn’t use is_uploaded_file() and just read the file defined by $this->data['MyFile']['File']['tmp_name']. This allowed you to load a file from the server into the database and to retrieve it later. For example: if you knew the path to app/config/database.php, you could retrieve user name and password for the database…

    Hope that answers your question!

  • File Upload mit Cakephp | milkstyle Webdesign Blog

    [...] File Upload mit Cakephp ist sehr gut in diesem Blogeintrag beschrieben. Eine weitere interessante Methode habe ich in der Bakery [...]

  • Eddie

    Great article! This just what I needed for my an application I am building for a university. Users mostly upload doc files which are small enough.
    The best bonus is the dependency for models which means cake will cascade delete when I remove any projects!

  • cakebaker

    @Eddie: I’m glad this article was helpful for you :)

  • Eddie

    Me again. I was getting annoyed by how long it took to download files, (40 seconds for a small pdf)and realized that my browser was hanging because it expected more information to be coming. I commented out the header…
    //header('Content-length: ' . $file['ProjectFile']['size']);
    and the speed literally jumped.
    If anyone is realizing that downloads are much slower than uploads, give it a shot.

  • cakebaker

    @Eddie: Thanks for this tip!

  • BBigs

    Thanks for this its been a big help. I ran into a problem that in Safari the file input doesn’t show up in the params array. Any ideas on how to make it work? Thanks again.

  • BBigs

    Found the problem. I didn’t have the enctype set.

  • cakebaker

    @BBigs: I’m glad you could fix your problem :)

  • Sir P

    Useful notes here, its one of my favourite bookmarks for cake development… straight to point. Thanks for your hard work and time :)

  • cakebaker

    @Sir P: You are welcome!

  • Pylons + tw.forms: Upload file « वीर
  • the Troll Box » Blog Archive » ETL, Image vs File, Servana

    [...] Spent several hours reading pros and cons of storing images in the database vs storing them in the file system.  I don’t see a clear cut winner either way, but since I am working on a project in CakePHP, I will follow the steps laid out at File Upload with CakePHP. [...]

  • Serving Files from a database with CakePHP | Edward A. Webb (.com)

    [...] 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 [...]

  • 3. Subir imágenes al servidor y seguridad: UploadComponent « Tutorial CakePHP informal, sobre la marcha. ¡Participa!

    [...] una carpeta del servidor y guardarlas directamente en la base de datos. Aunque seguí en CakeBaker esta explicación de Daniel Hofstetter, uno de los colaboradores del proyecto CakePHP, al final me inspiré en la [...]

  • Carlos

    Hi cakebaker very good article, i’m using your code and my display image function is:

    function display($id)
       {
          Configure::write('debug', 0);
          $file = $this->MyFile->findById($id);
          echo $file['MyFile']['data'];
    
          exit();
       }

    But it looks that images are loading a too slow…here’s a page working with this function: http://algarve.property-algarve.co.uk/
    Can you help me improving the load speed or this is normal because the images are in the database? Thank you

  • cakebaker

    @Carlos: Yes, having the images in the database is slower, and especially if you have many images the overhead may be too much. Did you compare the load time of having the images in the database vs. having the images in the file system?

    Btw: you should set the correct content type for the files in the display() method, otherwise the browsers have problems with displaying the images, see for example http://algarve.property-algarve.co.uk/my_files/display/6877

  • Jon

    I found an interesting problem. The script mostly works perfectly for me, but after I download a file and try to click another link in my application, I get a “cannot modify headers — headers already sent” error.

    It goes like this: Load add->upload file->upload successful->download file->download successful->load new page->error.

    Anybody else run into this?

  • Jon

    Well, I really don’t know why, but commenting out the following line seems to have fixed my bug.

    // header(‘Content-length: ‘ . $file['ProjectFile']['size']);

    This is found in the download function. Someone else commented that it also speeds up their downloads.

  • cakebaker

    @Jon: Thanks for your comments!

    I’m glad you could fix your issue in the meantime. I added a hint to the download() method.

  • Jon

    Just so everyone is aware, if you use the below code to display more than one image on a view, it will reset Cake’s session.

    <img src="/yourcontroller/displayimageaction/12" />

    Note: This is only true if CakePHP security is set to HIGH. If it is MEDIUM or below, it should work fine. I’m currently trying to make it work with the HIGH security level.

  • cakebaker

    @Jon: Thanks for this hint, I wasn’t aware of this behavior. At the moment I don’t see a solution for how you could prevent the reset of the session…

  • Norland

    this example is very simple but powerful!

    point out many key points,

    help me quickly build my own up/dl model

    thanks.

  • Jon

    Just found a bug today in IE 7 that isn’t allowing the download function to work well with images. It works fine in other browsers, of course.

    Judging by Google results and some testing, it seems to have something to do with the Content-Disposition: attachment header. Anyone else experience this?

  • cakebaker

    @Norland: You are welcome!

    @Jon: It seems like you have to set some caching headers, see the following comment: http://ch.php.net/manual/en/function.header.php#88038.

    Hope it helps!

  • Jon

    Thanks cakebaker. I checked the link you sent me long ago but I missed that post.

  • cakebaker

    @Jon: Good to hear the link was helpful for you!

  • Bharani Mani

    Hello,
    I m new to Cakephp.. I have done Blog tutorial and few other examples. I need to use the controler’s action in my url. But I dont know how to bring up the view file directly.. http://localhost/cakephp/views/my_file/add.ctp?..

    Thanks,
    Bharani

  • cakebaker

    @Bharani: Thanks for your comment!

    Hm, I don’t understand what you try do accomplish… You can’t access view files via an URL, you have to request a controller action and this action then renders the respective view. Have a look at A Typical CakePHP Request to get a feeling of how CakePHP works.

    Hope that helps!

  • Rahul

    Hi.. i have used your code in my application. and it is working fine for file uploads upto 500 kB. i am unable to upload a file of size 1MB or more. i have also tried changing the datatype of field ‘data’ to longblob but it didnt work out. Any help would be highly appreciable. Also i need help how can i store my uploaded files in a folder instead of database.

  • cakebaker

    @Rahul: Hm, did you check “upload_max_filesize” in your php.ini? And if you want to store the file in the file system, you could use something like:

    move_uploaded_file($this->data['MyFile']['File']['tmp_name'], '/path/and/filename/of/destination');
    

    Hope that helps!

  • Rahul

    Thanx cakebaker for the help. I checked my php.ini file for the “upload_max_filesize” and it shows 128M that is perfectly fine. I am using Xampp server. pls do the needful. moreover i am unable to move my uploaded files to the destination i have used in the code below. any other hint why am i not able to get it through.

    $filename = $this->data['MyFile']['File']['tmp_name'];
    
    move_uploaded_file($this->data['MyFile']['File']['tmp_name'],'/app/webroot/img/uploadedfiles/'.$filename);
  • cakebaker

    @Rahul: Hm, there are two other settings in php.ini which could affect file uploads: post_max_size and memory_limit (see http://ch.php.net/manual/en/ini.core.php#ini.post-max-size).

    I think move_uploaded_file() expects a full path (i.e. ‘/path/to/your/project/app/webroot/[..]‘) and not only the path from the project root.

    Hope that helps.

  • Rahul

    Hi Cakebaker… Thanks for your help. I tried your settings that is to be done to the php.ini file, but it didn’t work out as well. So i found a provision to this that i would like to share with you. I have problem with “max_allowed_packet ” in mysql’s my.ini file which was previously set to 1MB. So i modified it according to my needs. Thanks indeed for your help.

  • cakebaker

    @Rahul: Good to hear you could fix your issue :)

  • Ray

    This is awesome, thank you for this concise and delicious recipe for success.

    Storing things in the database is my employers mandate for security reasons, much appreciate the elegant way you showed us how to do it.

  • cakebaker

    @Ray: You are welcome!

  • Ray

    I needed to display the image in the page for another project and it wasn’t immediately apparent to me how to do that, so this is what i ended up doing:

    I added this functionin my controller:

    function displayArtifact($artifact_id, $artifact_type = 'Image'){
        $file = $this->Artifact->$artifact_type->find(array('artifact_id'=>$artifact_id));
    
        $this->layout="binary";
        $this->set('image',$file[$artifact_type]['data']);
      }

    I am processing several file types, so thats why i’m not specifying the image type. you could do for the controller function something like
    function displayArtifact($artifact_id){
        $file = $this->Image->findByID($artifact_id));
    
        $this->layout="binary";
        $this->set('image',$file[$artifact_type]['data']);
      }

    then i created a blank view, display_artifact.ctp

    <?php
    //not sure if i even need this file but its working so i'm not going to rock the boat
    ?>

    and a binary layout

    <?php
    header("Content-type: image/jpeg");
    echo $image;
    ?>

    Then to tie it all together, I just reference my empty view as the img src

    i’m sure others did this in a more concise way, so I’m just offering this up as it works for me. the ingredients for this also came from:
    http://edivad.wordpress.com/2007/04/17/cakephp-mysql-and-blob/

  • Ray

    ^ to reference empty view as img src

    <img src="http://mydomain.com/cakebox/artifacts/displayArtifact/93">

  • cakebaker

    @Ray: Thanks for your addition!

  • Subiendo archivos con CakePHP « Blas's Blog

    [...] (Esta es una traducción hecha por mí, del documento de Daniel Hofstetter, el articulo original esta aquí) [...]

  • Jeffrey Ropp

    +1 for a well written and extremely helpful tutorial.

  • cakebaker

    @Jeffrey: You are welcome!

  • abcd

    hi all,

    i hv uploaded files with mb size and stored them in database…then im retrieving them from database…bt i cant see images in IE…and its working on firefox better…its on local server…

    on live server…after submit to disp details,i m getting “webpage has expired”…in IE7 and firefox works better…

    can anyone tell me what can i do?

  • cakebaker

    @abcd: Hm, difficult to say what the problem could be. Maybe it helps to set the cache settings described in http://ch.php.net/manual/en/function.header.php#88038.

  • File upload in CakePHP « Max Korytko’s Blog

    [...] Filed under: CakePHP Framework — maxkorytko @ 9:26 pm Refer to this blog post: CakePHP file upload Leave a [...]

  • udhaya

    how to upload image in cake php from URL??

  • cakebaker

    @udhaya: It is very similar to what is shown in the article. When creating the form you have to remove the “file” type and you have to replace the file input field with a “normal” text field. In the controller you then have to work with fread and fopen.

    Hope this helps!

  • Manish

    Thanks

    Very useful topic,i got benefited when i had very need that. very useful.no error .i had just copy and past and its working.

    i have one request to when i need multiple image upload then it will work or not…..if possible reply me

    Thanks a lot.
    Manish

  • cakebaker

    @Manish: I’m glad this article was helpful for you!

    To upload multiple files you have to modify the code. In the view you have to use something like:

    echo $form->file('MyFile.0.File');
    echo $form->file('MyFile.1.File');

    In the controller you can then access the data like:
    $this->data['MyFile'][0]['File']['name'];
    $this->data['MyFile'][1]['File']['name'];

    Hope this helps!

  • Nick

    Hi,

    Would like to know if the data from the file inside the database, can searched for keywords.

    I have lot of pdf’s and docs(training manuals etc..,) I am currently working on pet project.. where i could upload all the files into database..so that i could search through the files…help my trainees find solutions faster.

    I would like to know if this can be achieved.
    upload files->search data inside the files -> and display the results..name of file so that i can download it..

    Thanks for a remarkable tutorial.

  • cakebaker

    @Nick: Thanks for your comment!

    No, uploaded files are stored as binary data and MySQL doesn’t know how to interpret those data… You would have to use something like Solr to index and search your PDFs.

    Hope that helps!

  • Ramanan

    HI, Thank for this code, it is working fine for file uploads..image has been saved in Mysql , but i wanna know ,how to retrieve an image on the Mysql and display the Images…

  • sathesh

    It’s very use full know to upload a file, but when i download the file using download() function, where i see the downloaded file in my server.

  • cakebaker

    @Ramanan: See an earlier comment of mine that should answer your question.

    @sathesh: Hm, I don’t understand what you mean with your question. If you download a file by accessing the download() action, the file will be stored on your computer, not on the server.

  • Bookmarks of This Week (6/20-6/27) - Sometime PHP

    [...] File upload with CakePHP – cakebaker [...]

  • Chasers

    CakeBaker First off great article, I know its been several years since you wrote up but I am using this in an application that I am building now. Sadly I am also having this issue where my data is being corrupted on upload or download not really sure which. I have tried using the addslashes() function. That didnt solve anything. I have checked all my files and I do not have any trailing spaces after any ?> in my files. Im really just not sure what else could be causing this. I have tried upload png, jpg, pdf, odt, and txt files. the only files that work are txt files. Any thoughts on what else could be causing this would be great.

    Using
    -PHP 5.3+
    -CakePHP 1.3
    -Apache/2.2.14 (Ubuntu)
    -MySQL client version: 5.1.41

  • Chasers

    Sorry I forgot to mention that when i look at the original code it appears to have spaces in it. when i look at the code after i download it all spaces are replaced with “”
    For kicks and giggles i tried a str replace on the output to replace all the “” with ” ” but that also did nothing.

  • cakebaker

    @Chasers: Hm, does it work if the uploaded text file contains some non-ASCII characters like äöü? My guess is that it is some encoding issue…

  • Urdesh Kumar

    This is very helpfull for uploading the file.thanks very much.

  • 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

Bake a comment




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

© daniel hofstetter. Licensed under a Creative Commons License