File upload with CakePHP

Published on April 15, 2006 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

138 comments baked

  • auto April 15, 2006 at 15:04

    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 April 15, 2006 at 18:19

    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 April 17, 2006 at 11:26

    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 July 05, 2006 at 15:56

    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 July 05, 2006 at 16:55

    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 July 06, 2006 at 17:56

    @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 July 12, 2006 at 04:08

    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 July 13, 2006 at 15:49

    @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 July 13, 2006 at 23:10

    @cakebaker – Sweet! You rock! Thanks!

  • Lamby July 19, 2006 at 19:36

    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 July 19, 2006 at 19:38

    [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 July 31, 2006 at 00:42

    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 August 01, 2006 at 06:00

    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 August 01, 2006 at 09:17

    @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 August 04, 2006 at 00:16

    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 August 05, 2006 at 14:20

    @Lamby: I have fixed it in the post.

  • JP August 05, 2006 at 23:51

    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 August 08, 2006 at 10:58

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

  • elswidi August 10, 2006 at 15:16

    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 August 10, 2006 at 22:53

    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 August 11, 2006 at 14:32

    @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 August 27, 2006 at 15:24

    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 August 30, 2006 at 07:59

    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 November 07, 2006 at 08:25

    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 November 07, 2006 at 18:48

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

  • Tom December 20, 2006 at 20:26

    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 December 21, 2006 at 11:34

    @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 December 27, 2006 at 09:03

    [...] 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 January 20, 2007 at 17:51

    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 January 22, 2007 at 09:20

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

  • Peeter February 02, 2007 at 15:47

    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 100×10meg 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 February 03, 2007 at 11:32

    @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 March 06, 2007 at 02:27

    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… March 14, 2007 at 03:09

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

  • Marcos May 13, 2007 at 18:10

    Does anybody know why I can upload only small files (

  • cakebaker May 14, 2007 at 16:34

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

  • pabs May 15, 2007 at 08:50

    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 May 16, 2007 at 10:32

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

  • Wilhelm Raab June 07, 2007 at 22:47

    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 June 09, 2007 at 11:15

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

  • Bahari June 21, 2007 at 09:37

    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 June 26, 2007 at 18:45

    @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 June 28, 2007 at 10:46

    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 June 28, 2007 at 19:22

    @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 July 03, 2007 at 04:58

    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 July 04, 2007 at 07:36

    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 July 04, 2007 at 08:39

    @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 July 18, 2007 at 14:44

    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 July 19, 2007 at 17:02

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

  • confuzus July 27, 2007 at 18:23

    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 July 28, 2007 at 10:27

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

  • Kums August 06, 2007 at 15:28

    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 August 08, 2007 at 16:51

    @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 September 17, 2007 at 06:00

    How do I validate a file type?

  • cakebaker September 17, 2007 at 18:31

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

    Hope that helps :)

  • Jamie Carruthers September 29, 2007 at 10:49

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

  • cakebaker September 29, 2007 at 11:33

    @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 October 12, 2007 at 19:14

    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 October 15, 2007 at 15:06

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

  • Tim Daldini October 16, 2007 at 01:23

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

    Thanks

  • cakebaker October 16, 2007 at 17:16

    @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 November 05, 2007 at 08:43

    Im getting,

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

    Using Cake 1.2

    Any help?

  • kodiew November 05, 2007 at 09:28

    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 November 05, 2007 at 12:02

    @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 November 05, 2007 at 12:17

    @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 November 06, 2007 at 18:27

    @Helmut: Thanks for your comment!

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

  • kodiewix November 10, 2007 at 06:13

    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 November 11, 2007 at 14:24

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

  • cakebaker November 11, 2007 at 18:38

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

  • mary November 26, 2007 at 12:59

    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 December 06, 2007 at 20:13

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

  • Adam December 06, 2007 at 20:54

    @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 December 06, 2007 at 20:56

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

  • cakebaker December 11, 2007 at 18:13

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

  • fmic December 16, 2007 at 22:47

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

  • cakebaker December 17, 2007 at 08:54

    @fmic: The article covers both versions.

  • A few things about CakePHP, PostgreSQL and Xapian « Dokeos lead developer’s Weblog January 19, 2008 at 00:33

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

  • ben February 25, 2008 at 21:43

    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 February 26, 2008 at 18:30

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

  • sbeam March 13, 2008 at 04:44

    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 March 14, 2008 at 19:50

    @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 April 12, 2008 at 11:12

    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 April 13, 2008 at 18:56

    @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 April 23, 2008 at 04:39

    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 April 23, 2008 at 18:36

    @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 April 25, 2008 at 03:27

    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 April 27, 2008 at 09:22

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

  • Paul May 15, 2008 at 04:44

    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 May 15, 2008 at 11:07

    @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 May 26, 2008 at 23:22

    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 May 27, 2008 at 17:53

    @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 June 09, 2008 at 05:21

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

  • Eddie July 22, 2008 at 01:37

    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 July 22, 2008 at 17:07

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

  • Eddie July 24, 2008 at 02:38

    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 July 24, 2008 at 17:01

    @Eddie: Thanks for this tip!

  • BBigs July 31, 2008 at 05:59

    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 July 31, 2008 at 06:27

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

  • cakebaker July 31, 2008 at 19:20

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

  • Sir P August 23, 2008 at 23:11

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

  • cakebaker August 25, 2008 at 17:25

    @Sir P: You are welcome!

  • Pylons + tw.forms: Upload file « वीर August 26, 2008 at 14:01
  • the Troll Box » Blog Archive » ETL, Image vs File, Servana September 17, 2008 at 23:33

    [...] 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) October 03, 2008 at 18:30

    [...] 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! January 09, 2009 at 14:09

    [...] 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 February 06, 2009 at 20:48

    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 February 10, 2009 at 18:05

    @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 February 20, 2009 at 01:49

    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 February 20, 2009 at 02:04

    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 February 20, 2009 at 17:53

    @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 March 16, 2009 at 22:07

    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 March 17, 2009 at 17:16

    @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 March 20, 2009 at 08:11

    this example is very simple but powerful!

    point out many key points,

    help me quickly build my own up/dl model

    thanks.

  • Jon March 20, 2009 at 17:51

    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 March 21, 2009 at 17:59

    @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 March 29, 2009 at 02:36

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

  • cakebaker March 31, 2009 at 16:57

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

  • Bharani Mani April 17, 2009 at 09:56

    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 April 17, 2009 at 16:24

    @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 November 10, 2009 at 16:36

    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 November 10, 2009 at 17:42

    @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 November 11, 2009 at 10:01

    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 November 11, 2009 at 17:21

    @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 November 12, 2009 at 14:26

    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 November 12, 2009 at 17:45

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

  • Ray December 02, 2009 at 08:39

    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 December 02, 2009 at 18:16

    @Ray: You are welcome!

  • Ray December 27, 2009 at 09:22

    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 December 27, 2009 at 09:28

    ^ to reference empty view as img src

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

  • cakebaker December 28, 2009 at 17:45

    @Ray: Thanks for your addition!

  • Subiendo archivos con CakePHP « Blas's Blog January 13, 2010 at 17:59

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

  • Jeffrey Ropp January 13, 2010 at 19:37

    +1 for a well written and extremely helpful tutorial.

  • cakebaker January 14, 2010 at 17:42

    @Jeffrey: You are welcome!

  • abcd February 09, 2010 at 20:43

    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 February 10, 2010 at 17:40

    @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 March 10, 2010 at 23:27

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

  • udhaya March 11, 2010 at 12:26

    how to upload image in cake php from URL??

  • cakebaker March 11, 2010 at 17:41

    @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!

Bake a comment




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

© daniel hofstetter. Licensed under a Creative Commons License