Posted by: sabbour on: July 18, 2008
7/7/2009.
Component code updated on 30/4/2009, check the posted code or the download links.
I revisited the code I used to develop the old image resize component because I found a bug in the resize code. I used the resize function from the PImageComponent by Wendy and ended up with the current version I’m using at my websites:
Code download at the bottom of the post.
<?php
/*
File: /app/controllers/components/image.php
*/
class ImageComponent extends Object
{
/*
* Uploads an image and its thumbnail into $folderName/big and $folderName/small respectivley.
* Also uploads a zoom cropped image into $folderName/home. You could easily modify it to suit your needs!
* Directions:
* In view where you upload the image, make sure your form creation is similar to the following
* <?= $form->create('ControllerName',array('type' => 'file')); ?>
* In view where you upload the image, make sure that you have a file input similar to the following
* <?= $form->file('Image/image1'); ?>
* In the controller, add the component to your components array
* var $components = array("Image");
* In your controller action (the parameters are expained below)
* $image_path = $this->Image->upload_image_and_thumbnail($this->data,"name1", 573,380,80,80, "sets");
* this returns the file name of the result image. You can store this file name in the database
*
* Note that your image will be stored in 3 locations:
* Image: /webroot/img/$folderName/big/$image_path
* Thumbnail: /webroot/img/$folderName/small/$image_path
* Homepage: /webroot/img/$folderName/home/$image_path
*
* You could easily add more locations or remove locations you don't need
* Finally in the view where you want to see the images
* <?= $html->image('sets/big/'.$furnitureSet['FurnitureSet']['image_path']);
* where "sets" is the folder name we saved our pictures in, and $furnitureSet['FurnitureSet']['image_path'] is the file name we stored in the database
* Parameters:
* $data: the image data array from the form
* $maxw: the maximum width that you want your picture to be resized to
* $maxh: the maximum width that you want your picture to be resized to
* $thumbscalew: the maximum width hat you want your thumbnail to be resized to
* $thumbscaleh: the maximum height that you want your thumbnail to be resized to
* $folderName: the name of the parent folder of the images. The images will be stored to /webroot/img/$folderName/big/ and /webroot/img/$folderName/small/
*/
function upload_image_and_thumbnail($data, $maxw, $maxh, $thumbscalew, $thumbscaleh, $folderName) {
if (strlen($data['name'])>4){
$error = 0;
$tempuploaddir = "img/temp"; // the /temp/ directory, should delete the image after we upload
$homeuploaddir = "img/".$folderName."/home"; // the /home/ directory
$biguploaddir = "img/".$folderName."/big"; // the /big/ directory
$smalluploaddir = "img/".$folderName."/small"; // the /small/ directory for thumbnails
// Make sure the required directories exist, and create them if necessary
if(!is_dir($tempuploaddir)) mkdir($tempuploaddir,0755,true);
if(!is_dir($homeuploaddir)) mkdir($homeuploaddir,0755,true);
if(!is_dir($biguploaddir)) mkdir($biguploaddir,0755,true);
if(!is_dir($smalluploaddir)) mkdir($smalluploaddir,0755,true);
$filetype = $this->getFileExtension($data['name']);
$filetype = strtolower($filetype);
if (($filetype != "jpeg") && ($filetype != "jpg") && ($filetype != "gif") && ($filetype != "png"))
{
// verify the extension
return;
}
else
{
// Get the image size
$imgsize = GetImageSize($data['tmp_name']);
}
// Generate a unique name for the image (from the timestamp)
//$id_unic = str_replace(".", "", strtotime ("now"));
$id_unic = $uuid = String::uuid();
$filename = $id_unic;
settype($filename,"string");
$filename.= ".";
$filename.=$filetype;
$tempfile = $tempuploaddir . "/$filename";
$homefile = $homeuploaddir . "/$filename";
$resizedfile = $biguploaddir . "/$filename";
$croppedfile = $smalluploaddir . "/$filename";
if (is_uploaded_file($data['tmp_name']))
{
// Copy the image into the temporary directory
if (!copy($data['tmp_name'],"$tempfile"))
{
//print "Error Uploading File!.";
unset($filename);
unlink($tempfile);
exit();
}
else {
/*
* Generate the home page version of the image center cropped
*/
$this->resizeImage('resizeCrop', $tempuploaddir, $filename, $homeuploaddir, $filename, 886, 473, 85);
/*
* Generate the big version of the image with max of $imgscale in either directions
*/
$this->resizeImage('resize', $tempuploaddir, $filename, $biguploaddir, $filename, $maxw, $maxh, 85);
/*
* Generate the small thumbnail version of the image with scale of $thumbscalew and $thumbscaleh
*/
$this->resizeImage('resizeCrop', $tempuploaddir, $filename, $smalluploaddir, $filename, $thumbscalew, $thumbscaleh, 75);
// Delete the temporary image
unlink($tempfile);
}
}
// Image uploaded, return the file name
return $filename;
}
}
/*
* Deletes the image and its associated thumbnail
* Example in controller action: $this->Image->delete_image("1210632285.jpg","sets");
*
* Parameters:
* $filename: The file name of the image
* $folderName: the name of the parent folder of the images. The images will be stored to /webroot/img/$folderName/big/ and /webroot/img/$folderName/small/
*/
function delete_image($filename,$folderName) {
if(is_file("img/".$folderName."/home/".$filename))
unlink("img/".$folderName."/home/".$filename);
if(is_file("img/".$folderName."/big/".$filename))
unlink("img/".$folderName."/big/".$filename);
if(is_file("img/".$folderName."/small/".$filename))
unlink("img/".$folderName."/small/".$filename);
}
function getFileExtension($str) {
$i = strrpos($str,".");
if (!$i) { return ""; }
$l = strlen($str) - $i;
$ext = substr($str,$i+1,$l);
return $ext;
}
/*
* @param $cType - the conversion type: resize (default), resizeCrop (square), crop (from center)
* @param $id - image filename
* @param $imgFolder - the folder where the image is
* @param $newName - include extension (if desired)
* @param $newWidth - the max width or crop width
* @param $newHeight - the max height or crop height
* @param $quality - the quality of the image
* @param $bgcolor - this was from a previous option that was removed, but required for backward compatibility
*/
function resizeImage($cType = 'resize', $srcfolder, $srcname, $dstfolder, $dstname = false, $newWidth=false, $newHeight=false, $quality = 75)
{
$srcimg = $srcfolder.DS.$srcname;
list($oldWidth, $oldHeight, $type) = getimagesize($srcimg);
$ext = $this->image_type_to_extension($type);
//check to make sure that the file is writeable, if so, create destination image (temp image)
if (is_writeable($dstfolder))
{
$dstimg = $dstfolder.DS.$dstname;
}
else
{
//if not let developer know
debug("You must allow proper permissions for image processing. And the folder has to be writable.");
debug("Run \"chmod 777 on '$dstfolder' folder\"");
exit();
}
//check to make sure that something is requested, otherwise there is nothing to resize.
//although, could create option for quality only
if ($newWidth OR $newHeight)
{
/*
* check to make sure temp file doesn't exist from a mistake or system hang up.
* If so delete.
*/
if(file_exists($dstimg))
{
unlink($dstimg);
}
else
{
switch ($cType){
default:
case 'resize':
# Maintains the aspect ration of the image and makes sure that it fits
# within the maxW(newWidth) and maxH(newHeight) (thus some side will be smaller)
$widthScale = 2;
$heightScale = 2;
// Check to see that we are not over resizing, otherwise, set the new scale
if($newWidth) {
if($newWidth > $oldWidth) $newWidth = $oldWidth;
$widthScale = $newWidth / $oldWidth;
}
if($newHeight) {
if($newHeight > $oldHeight) $newHeight = $oldHeight;
$heightScale = $newHeight / $oldHeight;
}
//debug("W: $widthScale H: $heightScale<br>");
if($widthScale < $heightScale) {
$maxWidth = $newWidth;
$maxHeight = false;
} elseif ($widthScale > $heightScale ) {
$maxHeight = $newHeight;
$maxWidth = false;
} else {
$maxHeight = $newHeight;
$maxWidth = $newWidth;
}
if($maxWidth > $maxHeight){
$applyWidth = $maxWidth;
$applyHeight = ($oldHeight*$applyWidth)/$oldWidth;
} elseif ($maxHeight > $maxWidth) {
$applyHeight = $maxHeight;
$applyWidth = ($applyHeight*$oldWidth)/$oldHeight;
} else {
$applyWidth = $maxWidth;
$applyHeight = $maxHeight;
}
$startX = 0;
$startY = 0;
break;
case 'resizeCrop':
// Check to see that we are not over resizing, otherwise, set the new scale
// -- resize to max, then crop to center
if($newWidth > $oldWidth) $newWidth = $oldWidth;
$ratioX = $newWidth / $oldWidth;
if($newHeight > $oldHeight) $newHeight = $oldHeight;
$ratioY = $newHeight / $oldHeight;
if ($ratioX < $ratioY) {
$startX = round(($oldWidth - ($newWidth / $ratioY))/2);
$startY = 0;
$oldWidth = round($newWidth / $ratioY);
$oldHeight = $oldHeight;
} else {
$startX = 0;
$startY = round(($oldHeight - ($newHeight / $ratioX))/2);
$oldWidth = $oldWidth;
$oldHeight = round($newHeight / $ratioX);
}
$applyWidth = $newWidth;
$applyHeight = $newHeight;
break;
case 'crop':
// -- a straight centered crop
$startY = ($oldHeight - $newHeight)/2;
$startX = ($oldWidth - $newWidth)/2;
$oldHeight = $newHeight;
$applyHeight = $newHeight;
$oldWidth = $newWidth;
$applyWidth = $newWidth;
break;
}
switch($ext)
{
case 'gif' :
$oldImage = imagecreatefromgif($srcimg);
break;
case 'png' :
$oldImage = imagecreatefrompng($srcimg);
break;
case 'jpg' :
case 'jpeg' :
$oldImage = imagecreatefromjpeg($srcimg);
break;
default :
//image type is not a possible option
return false;
break;
}
//create new image
$newImage = imagecreatetruecolor($applyWidth, $applyHeight);
//put old image on top of new image
imagecopyresampled($newImage, $oldImage, 0,0 , $startX, $startY, $applyWidth, $applyHeight, $oldWidth, $oldHeight);
switch($ext)
{
case 'gif' :
imagegif($newImage, $dstimg, $quality);
break;
case 'png' :
imagepng($newImage, $dstimg, $quality);
break;
case 'jpg' :
case 'jpeg' :
imagejpeg($newImage, $dstimg, $quality);
break;
default :
return false;
break;
}
imagedestroy($newImage);
imagedestroy($oldImage);
return true;
}
} else {
return false;
}
}
function image_type_to_extension($imagetype)
{
if(empty($imagetype)) return false;
switch($imagetype)
{
case IMAGETYPE_GIF : return 'gif';
case IMAGETYPE_JPEG : return 'jpg';
case IMAGETYPE_PNG : return 'png';
case IMAGETYPE_SWF : return 'swf';
case IMAGETYPE_PSD : return 'psd';
case IMAGETYPE_BMP : return 'bmp';
case IMAGETYPE_TIFF_II : return 'tiff';
case IMAGETYPE_TIFF_MM : return 'tiff';
case IMAGETYPE_JPC : return 'jpc';
case IMAGETYPE_JP2 : return 'jp2';
case IMAGETYPE_JPX : return 'jpf';
case IMAGETYPE_JB2 : return 'jb2';
case IMAGETYPE_SWC : return 'swc';
case IMAGETYPE_IFF : return 'aiff';
case IMAGETYPE_WBMP : return 'wbmp';
case IMAGETYPE_XBM : return 'xbm';
default : return false;
}
}
}
?>
to use it in your controller,
<?php
class FurnitureSetsController extends AppController {
var $name = 'FurnitureSets';
var $components = array("Image","RequestHandler");
function admin_add() {
if (!empty($this->data)) {
$this->FurnitureSet->create();
if ($this->FurnitureSet->save($this->data)) {
// resize the image to 573x380 and create a square thumbnail 80x80
$image_path = $this->Image->upload_image_and_thumbnail($this->data,"name1", 573,380,80,80, "sets");
if(isset($image_path)) {
$this->FurnitureSet->saveField('image_path',$image_path);
}
else {
$this->Session->setFlash(__('The image for the set could not be saved. Please, try again.', true));
}
}
}
}
Note that you can modify the resize settings in the upload_image_and_thumbnail method.
The view..
<div class="furnitureSets form">
<?php echo $form->create('FurnitureSet',array('type' => 'file'));?>
<fieldset>
<legend>
<?php __('Add Furniture set');?>
</legend>
<?php
echo $form->input('name');
echo $form->input('country_id');
?>
<div class="input">
<label for="Image/name1">Image</label>
<?php
echo $form->file('Image/name1', array('size' => '40'));
?>
</div>
<?php
echo $form->input('is_featured');
echo $form->input('furniture_material_id');
echo $form->input('furniture_category_id');
echo $form->input('is_indoor');
echo $form->input('is_outdoor');
echo $form->input('CustomerType');
?>
</fieldset>
<?php echo $form->end('Submit');?>
</div>
Now as promised, the code!
30/4/2009: after modification from comments
17/7/2008: Old version
Again, thanks Wendy for your code!
This works great! What a superbly easy component to use. The only thing that tripped me up was that my PHP config didn’t have the GD2 library running, but a quick tweak in the php.ini and all was well.
Good stuff! Should definitely consider putting this up on the Bakery or similar.
This works well, thanks! But what if I want to redirect after uploading an image? I get an “Headers already sent error message”, any ideas? thanks in advance.
Never mind, I had some whitespace after the image.php script, fixed now.
@sabbour:
Do you have the view for the FurnitureSetsController?
hi! error upload image png. fix: Insert on function resizeImage():
if( version_compare( phpversion(), “5.1.0″, “>=” ) ) {
$quality = 9;
}
I’ve been trying to get this compontent working for awhile now and I can’t seem to get it right. I am receiving back an image path and am not recieving any errors. I have chmod 777 my big small and home dir, but I still cannot get a single image to save even though I am receiving the image name. I have also tried to find the error and it seems like the if (is_uploaded_file($data['Image'][$datakey]['tmp_name']))
statement isn’t even firing properly. Any ideas?
nice work BUT
why dont you choose simple name for controllers /table/model ….. so that your code can be digested with out much efforts…
thanks
Excellent component…
just wanted to know if I want to do this using AJAX. I want to AVOID jquery for some reason. I tried SWF but cant get it working.
Any Ideas
once again thanks
ummm, is it just me or is that view/controller code all wrong.
First of all the form code renders as:
Should that name not be [Photo][Image][name1]?
(my model is called “Photo” not “FurnitureSet”)
and the part where you instantiate the Image: “upload_image_and_thumbnail($this->data,”name1″…
The component fails because you should be passing $this->data["Photo"], no?
I’m getting: Undefined index: Image.
Thanks
Opps your site strips tags… the form code above should be …
name=”data[Photo][Image/name1]”
should that not render as
name=”data[Photo][Image][name1]“?
Hey again,
Ya, it looks like in the latest version of Cake, 1.2.0.7692 RC3, you need to use the ‘dot’ notation in your view.
so,
echo $form->file(‘Image/name1′, array(’size’ => ‘40′));
becomes
echo $form->file(‘Image.name1′, array(’size’ => ‘40′));
and you do have to pass the image class:
$this->data["Photo"] (in my case Photo)
instead of:
$this->data
actually you can just pass: $this->data,
but you do have to use dot notation now.
sorry
Hey sabbour,
NICE WORK
Works flawlessly!
Here’s a note to whoever’s getting an “error” whereby you can’t see the file getting uploaded – I spent quite a while “debugging” the script wondering if it was the script’s error, but it turns out its a bug/config thing in XAMPP for Mac.
The default installation of XAMPP for Mac (is horribly crippled, compared to its Windows counterpart) – and hence you MUST manually set the upload_dir inside the php.ini file!
This is because PHP defaults to /tmp in Mac, but /tmp is not writable to all (i.e, NOT chmodded 777).
Hence, set the dir to upload to inside php.ini and all works flawlessly
PS: Strangely, “usual” PHP uploading works fine.. Maybe its a thing with cake, i don’t know
Regards,
Kelvin
Very nice!
But, pay attention in the block line 92..118
if (is_uploaded_file(..){
…
}
the return $filename must be INSIDE this block or a file that IS NOT UPLOADED give a true result.
-> put the “}” in line 118 below the return $filename
Hello, I’m using your component, thanks!
But, I think the image processing isn’t doing very all, I don’t know if it is a problem into your script. Some images simply are saved as invalid images, and I can’t see/open them.
Do your know what can that be?
Thanks!!!!!!!
thank you, greetings from Chile!
Wow! This component is very good! I have modified the code to create the folder that contains folders home, big and small.
$tempuploaddir = “img/temp”; // the /temp/ directory, should delete the image after we upload (no modified)
$rootuploaddir = “img/”.$folderName; // the root controller directory (added)
$homeuploaddir = “img/”.$folderName.”/home”; // the /home/ directory (no modified)
$biguploaddir = “img/”.$folderName.”/big”; // the /big/ directory (no modified)
$smalluploaddir = “img/”.$folderName.”/small”; // the /small/ directory for thumbnails (no modified)
// Make sure the required directories exist, and create them if necessary
if(!is_dir($rootuploaddir)) mkdir($rootuploaddir, 0777); // (added)
if(!is_dir($tempuploaddir)) mkdir($tempuploaddir, 0777); // (modified)
if(!is_dir($homeuploaddir)) mkdir($homeuploaddir, 0777); // (modified)
if(!is_dir($biguploaddir)) mkdir($biguploaddir, 0777); // (modified)
if(!is_dir($smalluploaddir)) mkdir($smalluploaddir, 0777); // (modified)
Thanks for the code, has been helpful.
hi!
first of all thank you for this component, it’s great! but now my problem: I’m using XAMPP for Windows and on my localhost it works just fine. When I upload it to the server I get the error Error Uploading File!. Why is it? I set my \app\temp directory writable (777) but it still doesn’t work? can anyone help me? thanks
ok, got it! I set the wrong temp directory writable. SORRY! It’s late and was watching soccer on tv along the way. but still, GREAT component!!
thank you, greetings from Brazil!
nice work dear..thanks
Excellent work!
A little tweaking to get it to do what i wanted, but works great.
Thanks alot!
Thanks a lot for this useful component!
Thank you very much for publishing that code i saved lot of my time. Greetings from Poland!
This is how I set mine up… WWW_ROOT and DS are global constants in cake; which are useful.
also the mkdir variables weren’t in the right place; so didn’t actually work recusively
class {
var $original_folder = "original";
var $thumb_folder = "thumb";
function upload(...) {
...
$tempuploaddir = WWW_ROOT."img".DS."temp"; // the /temp/ directory, should delete the image after we upload
$biguploaddir = WWW_ROOT."img".DS.$folderName.DS.$this->original_folder; // the /big/ directory
$smalluploaddir = WWW_ROOT."img".DS.$folderName.DS.$this->thumb_folder; // the /small/ directory for thumbnails
if(!is_dir($tempuploaddir)) mkdir($tempuploaddir,"0777",true);
if(!is_dir($biguploaddir)) mkdir($biguploaddir,"0777",true);
if(!is_dir($smalluploaddir)) mkdir($smalluploaddir,"0777",true);
...
}
...
}
Is it possible to resize the images to 72dpi?
Can anyone tel me what is Image in Image/name1 if name1 i is database table field ???
I can’t get this right!!!
Thank you for your answer!
I’m trying to develop a user friendly web site and i have tried all image upload scripts for cakephp published on web, and i write some myself but I’m not happy whit anyone of those. So far. My goal is to save a image_path into a database field named image_path(or so). If a user wants to delete or change it, the option is here for him(or her)…
Hi,
improvment of ninhhungs png fix (http://sabbour.wordpress.com/2008/07/18/enhanced-image-upload-component-for-cakephp-12/#comment-97):
imagepng($newImage, $dstimg, 9 – ($quality / 100.0) * 9);
this maps the quality paramter to the 0-9 range of imagepng’s quality setting (0 best, 9 worst).
I have used this successfully for multiple file uploads, but the filename generation has to be modified slightly. The reason is that if you try to upload multiple files at the same time (with the same form) it will generate the same name for (many) of them since it’s using the timestamp
In image.php, find the lines
$id_unic = str_replace(“.”, “”, strtotime (“now”));
to this you can add an extension depending on your image_path, for instance I’ve added (since my datakeys are named filedata_file1, filedata_file2, etc)
#to prevent naming conflicts when uploading multiple files simulaneously,
#changed to append last 3 chars of datakey to filename
$index_of = stripos($datakey, “_”);
$filename = $id_unic . substr($datakey, $index_of, strlen($datakey)-$index_of);
May I upload it to github.com under the MIT license, so as to have it easily mantained on a public repostory?
Thanks in advance;
Tute.
If i store the path in database, resize is inposible?
I am suddenly having a problem displaying png files, even though I’m doing exactly the same as for other types (jpegs displays well for example). All the paths are the same.
Any ideas why pngs don’t display?
Hello, thnx for this component. I am newbie in cakephp and trying to use it (the github version). The problem is that no file is uploaded in the “files” directory, however that directory is created, and thumbnails are nicely uploaded. Also the file name is being saved in database. Can you please help me? I am using xampp, windows xp, cakephp version 1.2.x.x_02.08.2009.
OK, I have make things done. Though now facing the problem of uploading large files (>10MB) caused by gd. May be I have to spend more time with it. Thanks anyway.
@Victor, the PNG problem was that it’s quality range is [0..9], not [0..100]. Changing line 281 solves the problem:
- imagepng($newImage, $dstimg, $quality);
+ imagepng($newImage, $dstimg, round($quality/10));
Great component, using it-.
You must update y’r comments for resizeImage function in file:
/*
* @param $cType – the conversion type: resize (default), resizeCrop (square), crop (from center)
* @param $id – image filename
* @param $imgFolder – the folder where the image is
* @param $newName – include extension (if desired)
* @param $newWidth – the max width or crop width
* @param $newHeight – the max height or crop height
* @param $quality – the quality of the image
* @param $bgcolor – this was from a previous option that was removed, but required for backward compatibility
*/
function resizeImage($cType = ‘resize’, $srcfolder, $srcname, $dstfolder, $dstname = false, $newWidth=false, $newHeight=false, $quality = 75) {
Muchas Gracias!!!! Thanks So much!!!! Saludos desde Colombia!!!!
Using this in a ‘gallery_controller’, how would i use this to edit the gallery details, and upload multiple images in to an image mysql table related to that gallery???
Nice job! There is a small typo in your view as already said in the comments:
echo $form->file(‘Image/name1′, array(’size’ => ‘40′));
should be:
echo $form->file(‘Image.name1′, array(’size’ => ‘40′));
Hello
Kind of new to cakePhp … tryin to use thia component but i keep getting
Undefined index: name [APP\controllers\components\image.php, line 45]
Any idea … ?
Thanks
another thing is buggin me …
the signature for upload_image_and_thumbnail is
($data, $maxw, $maxh, $thumbscalew, $thumbscaleh, $folderName)
so why do u call
$this->Image->upload_image_and_thumbnail($this->data,”name1″, 573,380,80,80, “sets”
Am i missin something … ?
cheers
hello
got it all figured out, parts of thge problems i was experiencing had been pointed out in rpevious comments
…
As i was sayin, in the code reported at the beginning of the post there seems to be a mismatch between the signature of upload_image_and_thumbnail and its call in the controller.
To summarize the changes that did it for me, in case anyone was havin my same problems:
___
In
($this->data,”name1″, 573,380,80,80, “sets”)
“name1″ should be removed and $this->data replaced with $this->data["Image"]["name1"]
And in the view
echo $form->file(‘Image/name1′, array(’size’ => ‘40′));
should be
echo $form->file(‘Image.name1′, array(’size’ => ‘40′));
Please feedback if this is wrong ..
cheers
July 30, 2008 at 4:23 pm
Hi Ahmed, thanks for the nice and simple component. It works like a charm for me.