Image Upload Component (CakePHP)

Thanks to some fine work by Ben Borowski, uploading images in CakePHP is easy as kicking your kids. I’ve used this component for quite a few projects. It’s safe to say that it rules.

Learn how to use it, love it, code it.

There have been updates to this article. Please make sure you note any changes before commenting.

This article covers uploading one image at a time. To learn how to upload multiple images at once, see the Multiple Image Uploads Into Single MySQL Table (CakePHP) article.

Dropping The Knowledge (pretext)

Using the upload component is super simple and allows for optional input to customize the resulting image.

/*
  * upload
  * - handle uploads of any type
  *		@ file - a file (file to upload) $_FILES[FILE_NAME]
  *		@ path - string (where to upload to)
  *		@ name  [optional] - override the default file name
  *		@ rules [optional] - how to handle file types
  *			- rules['type'] = string ('resize','resizemin','resizecrop','crop')
  *			- rules['size'] = array (x, y) or single number
  *			- rules['output'] = string ('gif','png','jpg')
  *			- rules['quality'] = integer (quality of output image)
  *		@ allowed [optional] - allowed filetypes array
  *			- default: array ('jpg','jpeg','gif','png')
  *	ex:
  * 	$result = $this->Upload->upload($file, 'uploads', null, array('type' => 'resizecrop', 'size' => array('400', '300'), 'output' => 'jpg'));
  *
  */

Each value should be self explanatory. If you need further explanation, post a comment and we’ll go through it together.

1. Download Upload Component & Create Upload Dir

Download the following zip file, extract it, and place upload.php in your /app/controllers/components/ directory.
Download the complete image upload component here.

Now create an ‘uploads’ directory in /app/webroot/img/uploads/ and make sure to give the directory 777 (writable by all) permissions.

2. Prepare Your Controller

Next we’ll slap the necessary code in place to handle a single image upload. (To upload multiple images at once, check out this article.)

class ImagesController extends AppController {

	var $name = 'Images';
	var $helpers = array('Html', 'Form');
	var $components = array('Upload');

function upload() {

	if (empty($this->data)) {
		$this->render();
	} else {
		$this->cleanUpFields();

		// set the upload destination folder
		$destination = realpath('../../app/webroot/img/uploads/') . '/';

		// grab the file
		$file = $this->data['Image']['filedata'];

		// upload the image using the upload component
		$result = $this->Upload->upload($file, $destination, null, array('type' => 'resizecrop', 'size' => array('400', '300'), 'output' => 'jpg'));
		
		if (!$result){
			$this->data['Image']['filedata'] = $this->Upload->result;
		} else {
			// display error
			$errors = $this->Upload->errors;

			// piece together errors
			if(is_array($errors)){ $errors = implode("<br />",$errors); }

			$this->Session->setFlash($errors);
			$this->redirect('/images/upload');
			exit();
		}
		if ($this->Image->save($this->data)) {
			$this->Session->setFlash('Image has been added.');
			$this->redirect('/images/index');
		} else {
			$this->Session->setFlash('Please correct errors below.');
			unlink($destination.$this->Upload->result);
		}
	}
}

* Highlighted code represents things relating to the upload class. The rest is default Cake code.

3. Create Your View

In the appropriate view controller (in this case, /app/views/images/upload.thtml), use the following code to insert a file upload field:

<?php echo $form->labelTag('Image/images', 'Image:' );?>
<?php echo $html->file('Image/filedata');?>

This will create the following HTML:

<label for="Image">Image:</label>
<input type="file" name="data[Image][filedata]" id="ImageFiledata" />	

Complete The Package

To make things easy, I’ve included all the above files in a single download. Get the complete CakePHP image upload package here.

If you have issues, comment. I’m happy to help.

Updates

Nov 8th, 2007: The uploads component (upload.php) has been modified to correctly handle filetypes.

Nov 6th, 2007: Errors are now gracefully displayed. An allowed variable has been added to the upload component regarding allowed filetypes. A .sql file has been included for easy setup of the images MySQL table.

Jan 16th, 2008: Thanks to Ruben, a small $fileType glitch was discovered… and fixed. Thanks Ruben! Jeff correctly pointed out that Ruben’s “fix” was actually an unneeded glitch. The component has been reverted to working form.

37 Comments so far

  1. hellyeahdude.com on October 26th, 2007

    Izzz nice, I like it!
    PHP Peice of CAKE!

  2. [...] Image Upload Component (CakePHP) [...]

  3. Koa on October 29th, 2007

    Thanks dude. The upload component download and package download links have been fixed. Download away!

  4. Alex on November 4th, 2007

    Howdy! Thanks for posting this code, it looks like it’ll be perfect for what I want to do. That said, I can’t get it to work, so I hope you can help me resolve a problem.

    I have downloaded the package you provided and placed each of the files in the correct place, but received a couple of errors:

    1. Missing Component Class
    When I hit /images/upload/ I receive a notice stating “the component class UploadComponent you have set in ImagesController can’t be found or doesn’t exist.” It also tells me to add

    class UploadComponent extends Object {
    }

    to app/controllers/components/upload.php. When I add it the error disappears and I am presented with the proper view.

    2. After following the steps in point 1, I can access the form, but when I submit the form I receive the error “Call to undefined function: upload() in [Path to Cake]/app/controllers/images_controller.php on line 22″ It seemed odd that I should have an empty UploadComponent as in point 1, so I changed the class name in upload.php from upload to UploadComponent which eliminated this error. I’m not sure if this is correct though. Should I have done something else?

    3. With the name change noted in step 2, I have eliminated the previous error, but now receive “Undefined index: quality” in upload.php on line 86. This was pretty easy to resolve, though I think you may need to modify your example above, which is missing the quality option for the results in the upload function in images_controller.php. I modified line 22 to fix the problem:

    $result = $this->Upload->upload($file, $destination, null, array('type' => 'resizecrop', 'size' => array('400', '300'), 'output' => 'jpg', 'quality' => '80'));

    So, the form now submits, and doesn’t throw any errors, but the file is not added to /app/webroot/img/uploads/ nor is a confirmation message shown in the flash. I verified that /uploads/ is set to 777. I’m running GD (2.0.28 compatible)

    Any help is greatly appreciated!

  5. Koa on November 4th, 2007

    Alex, the UploadComponent I included in the zip file was old and not written to be compatible with CakePHP. (Lame of me.)

    The zipped package has been fixed. Sorry for the mix up.

  6. Alex on November 4th, 2007

    Hi Koa, thanks for the speedy reply! I pulled down a fresh copy of the zipped package but in comparing the files I don’t see any differences. The issues I noted above are still present in the files. The timestamps on the files within the zip match between what I hd downloaded earlier today and the one I just pulled down.

    Oh, and I remembered one other error the final closing curly brace is missing from images_controller.php

    Cheers!

  7. Koa on November 4th, 2007

    All better.

    Thanks again Alex. At least one of us has eyes.

  8. Ronnie Herbine on November 5th, 2007

    Hi, great component. This is just what I needed. I was wondering if there is a way to specify a destination filename. I would like to prepend the user_id so people don’t overwrite each others files. Thanks again for the nice work.

  9. Moelle on November 6th, 2007

    This is really great. Thanks for the help.
    One question though: did you post the multiple upload component yet?
    I can really use it!

  10. Koa on November 6th, 2007

    Ronnie, the component automatically creates a unique filename by adding a unique number at the end of the filename.

    The function which handles this task is called uniquename, line 301 of the upload.php component.

  11. [...] article assumes you use the CakePHP image upload component I covered a while back. If you’re not using it, you should [...]

  12. Alex on November 6th, 2007

    Thanks Koa, the new zip addresses those problems, though for some reason the file isn’t saved to disk. Well, rather it is, but then immediately deleted due to the last bit of code in the controller:

    if ($this->Image->save($this->data)) {
    $this->Session->setFlash('Image has been added.');
    $this->redirect('/images/index');
    } else {
    $this->Session->setFlash('Please correct errors below.');
    unlink($destination.$this->Upload->result);
    }

    I’ve tracked the problem down to that one check, and if I comment out the unlink line all works as expected – the image is saved to the file system. I’m of a mind to eliminate that check and just save the file, but I thought I’d ask you before I do..?

    Also, the flash message never appeared, so it was a bit of fun trying to track down the root of the issue. is $this->Session->setFlash a 1.2.x way of doing things? If so, then I won’t worry about it for now as I am using 1.1.x. Otherwise, why would I use it instead of $this->flash? Trying to track down information about it hasn’t provided much useful info.

    Thanks again!

  13. Koa on November 6th, 2007

    Alex, I fixed the images_controller.php file to correctly save the data to the images MySQL table. You might have noticed that although the image saved when you uncommented the unlink code, the image name was not saved to MySQL.

    Secondly, $this->Session->setFlash is valid Cake 1.1.x code, but like most Cake variables, it’s not well documented. To use it, use the following in your /app/views/layouts/default.thtml:

    
    < ?php
       if ($session->check('Message.flash')): $session->flash(); endif;
       echo $content_for_layout;
    ?>
    

    Now instead of displaying messages on a seperate page, the message will appear on whatever page specified using $this->redirect().

    Hope this helps!

  14. Alex on November 8th, 2007

    Very cool, thanks!

    FYI, the pasted code includes curly single quotes which cause errors in PHP, but using a standard single quote fixes that problem.

    I’m goign to pull down the updated file, and once I have it working I’ll take a look at the multi-image version that you posted about recently.

    Cheers!

  15. jeet on November 19th, 2007

    When we upload the images its show “Warning: imagejpeg(): Unable to open ‘\/dracena0.jpg’ for writing in c:\apache2triad\htdocs\loco\app\controllers\components\upload.php on line 284 Warning: Cannot modify header information – headers already sent by (output started at c:\apache2triad\htdocs\loco\app\controllers\components\upload.php:284) in C:\apache2triad\htdocs\loco\cake\libs\controller\controller.php on line 447”

    Can you help me for upload images

  16. Koa on November 19th, 2007

    jeet, you need to make sure to give write permissions to the uploads folder – 777 should do it.

  17. Me on November 29th, 2007

    don’t forget to put enctype=”multipart/form-data” into the form tag or the file name is the only thing returned.

  18. Adam on November 30th, 2007

    I used to use Cake, although I came to the conclusion I despised it after a while. Not because it can code ideas faster than I can think of them, but on a superficial note I didn’t like the names it gave to HTML form elements so it could automate the saving and loading, but I also didn’t like the fact that it’s a VERY tightly coupled framework with low cohesion.

    We were discussing this over at TalkPHP.com the other week and came to the conclusion that one of the best frameworks for its high cohesion and loose coupling was in fact Zend Framework, and if I may say so myself, Zend Framework is definitely the one for me!

  19. Perkster on December 5th, 2007

    I’m having the same issue as jeet and I have given 777 permission to the img/uploads folder. Any suggestions?

  20. Koa on December 5th, 2007

    Perkster, I hate to sound like a broken record, but it must be a permission issue.

    Based on a quick Google search, I’m guessing that like jeet, you’re on a Windows machine. It seems like some Windows users have permission issues when trying to use PHP’s imagejpeg() function.

    I’ll try playing around with this in XP and post what I find.

  21. MamaCake on December 11th, 2007

    Great stuff! Problem though, not with this component, how do I use it? I guess it’s me of a general how do you use components question. I’ve gone through the component section in the manual and I don’t get it (sorry I’m slow and a bit impatient tonight).

    I am using it with a Product controller, that has the add, edit…etc methods. I want the fileupload on my product forms. How can I achieve this? Any help will be greatly appreciated!!!

  22. MamaCake on December 12th, 2007

    Duh!, never mind I’m all sorted. Thanks :)

  23. Clonix on December 18th, 2007

    Hi!

    Got the same prob than jeet
    (Permission denied [CORE/app/controllers/components/upload.php, line 284])
    Although I did ‘chmod 777 uploads’

    Any ideas?

  24. Dan on December 19th, 2007

    I’m getting the following response and I have no clue why. I’m on a Mac and I’ve set folders to 777.
    Please help—

    Warning: imagejpeg() [function.imagejpeg]: Unable to open ‘//20.jpg’ for writing: Permission denied in /Users/dan/Sites/archive/app/controllers/components/upload.php on line 284

    Warning: unlink(/) [function.unlink]: Is a directory in /Users/dan/Sites/archive/app/controllers/images_controller.php on line 163

  25. malik on December 21st, 2007

    hi koa,
    im having the same issues as jeet a few others. you’ve said you will be having a look at this script on xp, any luck so far?

    thanks

  26. Koa on December 21st, 2007

    malik, Dan, Clonix, etc:

    I hear your cries for help, but I cannot replicate the error, even on Windows XP. After looking at your errors above, I think your $destination variable is not being properly set.

    In your images_controller.php, try adding exit($destination); right after setting the $destination variable. That should echo the destination path when you try uploading an image. View it and make sure it’s correctly pointing to your uploads directory.

    I’ve tested this script on 11 different local machines and 8 servers with no problems. Hopefully each one of you will figure out what the hell is going on.

    Godspeed.

  27. malik on December 22nd, 2007

    hi koa
    i solved the problem i was having.
    heres the solution for the others who were having the same issue:

    inside the components/upload.php it requires the gd library. in windows some php installations dont have gd lib enabled in php.ini

    —————————————

    The php_gd2.dll is included in a standard PHP installation for Windows, it’s not enabled by default. You have to turn it on, the user may simply uncomment the line extension=php_gd2.dll in php.ini and restart the PHP extension.

    Change:

    #extension=php_gd2.dll

    To:

    extension=php_gd2.dll

    You may also have to correct the extension directory setting from:

    extension_dir = "./"

    Or:

    extension_dir = "./extensions"

    NOTE: SUBSTITUTE THE ACTUAL PHP INSTALLATION DIRECTORY ON *YOUR* COMPUTER.

    To (FOR WINDOWS):

    extension_dir = "c:/php/extensions"

    Thanks to Benoit Blais for this last point. Thanks also to Alan MacDougall and Perculator.

    —————

    for more information

    http://www.libgd.org/FAQ_PHP#How_do_I_get_gd_to_work_with_PHP.3F

    hope this is helpful

    thanks for the componet koa

  28. webMenace on January 8th, 2008

    Hey guys.. i get the following error:

    Parse error: syntax error, unexpected ‘;’, expecting T_FUNCTION in /usr/www/virtual/ljmenace/www.whitepridenation.com/app/models/image.php on line 47

    so its PROBABBBLYY my eyes playing tricks on me.. where am i forgetting to close something or ???

    Thanksss!

    PS GREAT HELP component by the way.

  29. DudeWheresMyLife on January 8th, 2008

    ya weird i actually get the same message!

    Parse error: syntax error, unexpected ‘;’, expecting T_FUNCTION in /usr/www/virtual/user/www.example.com/app/models/image.php on line 47

  30. Koa on January 8th, 2008

    malik, great work! Thank you for posting your solution.

  31. nil on January 15th, 2008

    Undefined index: Image [CORE\app\controllers\images_controller.php, line 19]

    when i m uploading any photo i get this error and photo is not saving in my mysql table.

    plz help me.

    Thanks

  32. Ruben on January 16th, 2008

    Great work! I just had to change

    if(!in_array($this->ext($fileName),$this->_allowed)){

    into

    if(!in_array($this->ext($fileType),$this->_allowed)){

  33. Jeff on January 20th, 2008

    Ok, figured out some things that I think someone may need to look at.

    I traced my problem to the upload.php component file. Specifically to the ext() function that determines what a image’s file extension is. apparently it is being fed the fileType not the fileName, the ext() function is looking for the fileName to determine the the extension.

    Now based on the comment by Ruben, that was changed from that for some reason.

    To give further info – this is running on an Apache 2.2 server being uploaded from FireFox.

    Hope this helps

  34. Koa on January 20th, 2008

    Jeff, please have a look at the updates above – I believe this problem has been fixed.

  35. Jeff on January 21st, 2008

    If you look at Ruben’s post it creates the problem.

    Where he takes

    if(!in_array($this->ext($fileName),$this->_allowed)){

    into

    if(!in_array($this->ext($fileType),$this->_allowed)){

    it should be that only reversed so that you change it from $fileType to $fileName

  36. Koa on January 24th, 2008

    Jeff, good call. Things have been changed back.

    This upload component has been used with over 15 projects without a problem. I’m going to assume whatever kinks existed upon release have pretty much been smoothed over.

    A big thanks goes out to everyone who contributed to building this killer component!

  37. [...] Image upload Component [...]