jQuery image upload & crop

By Thomas (klagoggle)
I was searching for a nice script with that i can upload and crop images.
I found a nice one @ http://www.webmotionuk.co.uk/php-jquery-image-upload-and-crop/ and made it cake ready ;)
So, i hope i can help you with my first Article!

Here we go:

Requirements


Installation


Ok, now we must create a new Component called "JqImgcrop".
Let`s create a new file called "jq_imgcrop.php" in yourapp/controllers/components/ with following content:

Component Class:

Download code <?php 
class JqImgcropComponent extends Object {

    function 
uploadImage($uploadedInfo$uploadTo$prefix){
        
$webpath $uploadTo;
        
$upload_dir WWW_ROOT.str_replace("/"DS$uploadTo);
        
$upload_path $upload_dir.DS;
        
$max_file "34457280";                         // Approx 30MB
        
$max_width 800;

        
$userfile_name $uploadedInfo['name'];
        
$userfile_tmp =  $uploadedInfo["tmp_name"];
        
$userfile_size $uploadedInfo["size"];
        
$filename $prefix.basename($uploadedInfo["name"]);
        
$file_ext substr($filenamestrrpos($filename".") + 1);
        
$uploadTarget $upload_path.$filename;

        if((!empty(
$uploadedInfo)) && ($uploadedInfo["error"] == 0)) {
            return 
false;
        }

        if (isset(
$uploadedInfo['name'])){
            
move_uploaded_file($userfile_tmp$uploadTarget );
            
chmod ($uploadTarget 0777);
            
$width $this->getWidth($uploadTarget);
            
$height $this->getHeight($uploadTarget);
            
// Scale the image if it is greater than the width set above
            
if ($width $max_width){
                
$scale $max_width/$width;
                
$uploaded $this->resizeImage($uploadTarget,$width,$height,$scale);
            }else{
                
$scale 1;
                
$uploaded $this->resizeImage($uploadTarget,$width,$height,$scale);
            }
        }
        return array(
'imagePath' => $webpath.$filename'imageName' => $filename'imageWidth' => $this->getWidth($uploadTarget), 'imageHeight' => $this->getHeight($uploadTarget));
    }

    function 
getHeight($image) {
        
$sizes getimagesize($image);
        
$height $sizes[1];
        return 
$height;
    }
    function 
getWidth($image) {
        
$sizes getimagesize($image);
        
$width $sizes[0];
        return 
$width;
    }

    function 
resizeImage($image,$width,$height,$scale) {
        
$newImageWidth ceil($width $scale);
        
$newImageHeight ceil($height $scale);
        
$newImage imagecreatetruecolor($newImageWidth,$newImageHeight);
$ext strtolower(substr(basename($image), strrpos(basename($image), ".") + 1));
        
$source "";
        if(
$ext == "png"){
            
$source imagecreatefrompng($image);
        }elseif(
$ext == "jpg" || $ext == "jpeg"){
            
$source imagecreatefromjpeg($image);
        }elseif(
$ext == "gif"){
            
$source imagecreatefromgif($image);
        }
        
imagecopyresampled($newImage,$source,0,0,0,0,$newImageWidth,$newImageHeight,$width,$height);
        
imagejpeg($newImage,$image,90);
        
chmod($image0777);
        return 
$image;
    }

    function 
resizeThumbnailImage($thumb_image_name$image$width$height$start_width$start_height$scale){
        
$newImageWidth ceil($width $scale);
        
$newImageHeight ceil($height $scale);
        
$newImage imagecreatetruecolor($newImageWidth,$newImageHeight);
        
$ext strtolower(substr(basename($image), strrpos(basename($image), ".") + 1));
        
$source "";
        if(
$ext == "png"){
            
$source imagecreatefrompng($image);
        }elseif(
$ext == "jpg" || $ext == "jpeg"){
            
$source imagecreatefromjpeg($image);
        }elseif(
$ext == "gif"){
            
$source imagecreatefromgif($image);
        }
        
imagecopyresampled($newImage,$source,0,0,$start_width,$start_height,$newImageWidth,$newImageHeight,$width,$height);
        
imagejpeg($newImage,$thumb_image_name,90);
        
chmod($thumb_image_name0777);
        return 
$thumb_image_name;
    }

    function 
cropImage($thumb_width$x1$y1$x2$y2$w$h$thumbLocation$imageLocation){
        
$scale $thumb_width/$w;
        
$cropped $this->resizeThumbnailImage(WWW_ROOT.str_replace("/"DS,$thumbLocation),WWW_ROOT.str_replace("/"DS,$imageLocation),$w,$h,$x1,$y1,$scale);
        return 
$cropped;
    }
}
?>


Then we must create a new helper called cropimage.php (you must create this file in yourapp/views/helpers/ )

Helper Class:

Download code <?php 
class CropimageHelper extends Helper {
    var 
$helpers = array('Html''Javascript''Form');

    function 
createJavaScript($imgW$imgH$thumbW$thumbH) {
            return 
$this->output("<script type=\"text/javascript\">
                function preview(img, selection) {
                    var scaleX = $thumbW / selection.width;
                    var scaleY = $thumbW / selection.height;

                    $('#thumbnail + div > img').css({
                        width: Math.round(scaleX * $imgW) + 'px',
                        height: Math.round(scaleY * $imgH) + 'px',
                        marginLeft: '-' + Math.round(scaleX * selection.x1) + 'px',
                        marginTop: '-' + Math.round(scaleY * selection.y1) + 'px'
                    });
                    $('#x1').val(selection.x1);
                    $('#y1').val(selection.y1);
                    $('#x2').val(selection.x2);
                    $('#y2').val(selection.y2);
                    $('#w').val(selection.width);
                    $('#h').val(selection.height);
                }

                $(document).ready(function () {
                    $('#save_thumb').click(function() {
                        var x1 = $('#x1').val();
                        var y1 = $('#y1').val();
                        var x2 = $('#x2').val();
                        var y2 = $('#y2').val();
                        var w = $('#w').val();
                        var h = $('#h').val();
                        if(x1==\"\" || y1==\"\" || x2==\"\" || y2==\"\"|| w==\"\" || h==\"\"){
                            alert('Please choose a area to crop...');
                            return false;
                        }else{
                            return true;
                    }
                });
            });

            $(window).load(function () {
                $('#thumbnail').imgAreaSelect({ aspectRatio: '1:1', onSelectChange: preview });
            });
            </script>"
);
    }

    function 
createForm($imagePath$tH$tW){
        
$x1 =         $this->Form->hidden('x1', array("value" => """id"=>"x1"));
            
$y1 =         $this->Form->hidden('y1', array("value" => """id"=>"y1"));
            
$x2 =         $this->Form->hidden('x2', array("value" => """id"=>"x2",));
            
$y2 =         $this->Form->hidden('y2', array("value" => """id"=>"y2"));
            
$w =             $this->Form->hidden('w', array("value" => """id"=>"w"));
            
$h =             $this->Form->hidden('h', array("value" => """id"=>"h"));
            
$imgP =      $this->Form->hidden('imagePath', array("value" => $imagePath));
            return 
$this->output("<img src=\"$imagePath\" style=\"float: left; margin-right: 10px;\" id=\"thumbnail\" alt=\"Create Thumbnail\" />
            <div style=\"position:relative; overflow:hidden; width:151px; height:151px;\">
                <img src=\"$imagePath\" style=\"position: relative;\" alt=\"Thumbnail Preview\" />
            </div>
            <br style=\"clear:both;\"/>$x1 $y1 $x2 $y2 $w $h $imgP"
);
    }
}
?>


Thats it, now i will tell you something about the

Usage


Ok, now you must add the component and the helper to your controller where you want to use the cropload ( :) )

Download code
    var $helpers = array(..., 'Cropimage')
    var $components = array(..., 'JqImgcrop');

Now you have to create a form where you can select a image.
I will give you an example:

Download code
<?php echo $form->create('YourModel', array('action' => 'createimage_step2'"enctype" => "multipart/form-data"));?>
    <?php
        
echo $form->input('name');
        echo 
$form->input('image',array("type" => "file")); 
        echo 
$form->end('Upload');
    
?>


Now, in the createimage_step2 function you have to add the upload function of our component:

Download code
$uploaded = $this->JqImgcrop->uploadImage($this->data['YourModel']['image'], '/img/upload/', 'prefix_');

and then push it to the view:
Download code
$this->set('uploaded',$uploaded);

This is what it looks in my controller:

Download code
    function createimage_step2(){
        if (!empty($this->data)) {
            $uploaded = $this->JqImgcrop->uploadImage($this->data['YourModel']['image'], '/img/upload/', 'prefix_');
            $this->set('uploaded',$uploaded);
        }
    

Now you have to add in your view ( in my case createimage_step2.ctp ) at the top the 2 Javascript libraries:

Download code
<?php
if(isset($javascript)):
        echo 
$javascript->link('jquery-1.2.6.min.js');
        echo 
$javascript->link('jquery.imgareaselect-0.4.2.min.js');
endif;
?>

Then you have to create a new form with the cropimage helper using:
Download code
echo $cropimage->createForm($uploaded["imagePath"], $width, $height);
The 2 width and height parameters specify the size of the thumbnail which will be created.

You have to close the form with a form->submit with the id "save_thumb" Download code
echo $form->submit('Done', array("id"=>"save_thumb"));

In my case it looks like this:

Download code
<?php 
        
echo $form->create('YourModel', array('action' => 'createimage_step3',"enctype" => "multipart/form-data"));    
        echo 
$form->input('id');
        echo 
$form->hidden('name');
        echo 
$cropimage->createForm($uploaded["imagePath"], 151151);
        echo 
$form->submit('Done', array("id"=>"save_thumb"));
echo 
$form->end();?>

Final Step

Now, in the createimage_step3 function in your controller we crop and save the image

Download code
$this->JqImgcrop->cropImage(151, $this->data['YourModel']['x1'], $this->data['YourModel']['y1'], $this->data['YourModel']['x2'], $this->data['YourModel']['y2'], $this->data['YourModel']['w'], $this->data['YourModel']['h'], $this->data['YourModel']['imagePath'], $this->data['YourModel']['imagePath'])

The function is using this pararmeters:
Download code
$thumb_width, $x1, $y1, $x2, $y2, $w, $h, $thumbLocation, $imageLocation
$thumb_width is the width of your thumbnail, $x1, $y1, $x2, $y2, $w and $h are including the crop parameters, $thumbLocation the location where you want to save your thumb, and $imageLocation the location of the sourceimage.

This function returns the filename of the created thumbnail.

Ok, that's it.

Big thanks to http://www.webmotionuk.co.uk for your great article!

 

Comments 693

CakePHP Team Comments Author Comments
 

Comment

1 Small corrections and adjustments

Thank you for the article.
I tryed it and managed to get it work for windows, running Cake 1.2 RC1 on XAMPP PHP 5.2.0

Corrections:

Under Usage:

  echo $form->input('image',"type" => "file");  
should be

  echo $form->input('image',array("type" => "file"));

To make it work for windows I needed to change the component


        if((!empty($uploadedInfo)) && ($uploadedInfo["error"] == 0)) {
            return false;
        } 

into


        if(empty($uploadedInfo)) {
            return false;
        } 

or remove it, as Error allways showed up 0, even on sucess.

Further I removed the two


 chmod ($uploadTarget , 0777); 
and
chmod($image, 0777);

lines.

My createimage_step2.ctp view needed


echo $cropimage->createJavaScript($uploaded['imageWidth'],$uploaded['imageHeight'],151,151);

to make the script run.

And as I am not running only this app, the image path showed up wrong. So I rewrote the createForm() in the cropimage.php helper, using the html helper to create the images with the correct pats.


 function createForm($imagePath, $tH, $tW){
        $x1 =         $this->Form->hidden('x1', array("value" => "", "id"=>"x1"));
            $y1 =         $this->Form->hidden('y1', array("value" => "", "id"=>"y1"));
            $x2 =         $this->Form->hidden('x2', array("value" => "", "id"=>"x2",));
            $y2 =         $this->Form->hidden('y2', array("value" => "", "id"=>"y2"));
            $w =             $this->Form->hidden('w', array("value" => "", "id"=>"w"));
            $h =             $this->Form->hidden('h', array("value" => "", "id"=>"h"));
            $imgP =      $this->Form->hidden('imagePath', array("value" => $imagePath));
            $imgTum = $this->Html->image($imagePath, array('style'=>'float: left; margin-right: 10px;', 'id'=>'thumbnail', 'alt'=>'Create Thumbnail'));
            $imgTumPrev = $this->Html->image($imagePath, array('style'=>'position: relative;', 'id'=>'thumbnail', 'alt'=>'Thumbnail Preview'));
            return $this->output("$imgTum
            <div style=\"position:relative; overflow:hidden; width:151px; height:151px;\">
                $imgTumPrev
            </div>
            <br style=\"clear:both;\"/>$x1 $y1 $x2 $y2 $w $h $imgP");
    }
Posted Jun 12, 2008 by Eskil Mjelva Saatvedt
 

Comment

2 really nice thing but not quite working

sure looks beautiful in the examples!
i even got it to work - partly - and only with the correctios of Eskil

the main problem is this crap about these DS-Seperators
once it works in createimage_part2, but then theres one Slash to much for _part3, this is on localhost (windows xp)
then i upload it on my webspace, now the slahes are the other way arround and still everything is just really messy and nothing actually works.

what can i do about it?
isn't there a nice function or something to be able to work with http paths like "http://localhost/cake/img/upload/pic.jpg"?
or even better with relative ones like "/cake/img/upload/pic.jpg"?
i used to do that with my upload scripts once..
this worked on localhost as well as in the www

in this case this whole thing about this paths "F:\xampp\htdocs\cake\app\webroot\/img/temp_pic.jpg" etc would't even occur.. or?

i mean, this relative and http paths always use this one forward slash: /

well, i tried about all kinds of things, i'm so tired of it.
i hope somebody can help

i already startet to put some constants in my bootstrap.php to get this thing fixed:

define('HTTP_HOST',$_SERVER['HTTP_HOST']);        // e.g. localhost
define('HTTP_BASE', 'http://'.HTTP_HOST.'/');     // e.g.    http://localhost/
define ('PATH_REL', '/home/cake/');    // e.g. /home/cake/
define ('PATH_HOME', 'http://'.HTTP_HOST.PATH_REL);      // e.g. http://localhost/home/cake/

but this can't be the intention of our Cake-Programmers to workaround it, can it?
Posted Jul 3, 2008 by Mark
 

Comment

3 Hmm

Will test it on my windows system and will try to submit a solution as soon as possible.

Thanks to Eskil and Mark.
Posted Jul 16, 2008 by Thomas
 

Comment

4 Very cool script

Hi,
just tested after some tweaking (thank you Eskil) it works on my Mac.

Note that I modified the cropImage return's to:
//return $cropped;
return $imageLocation;

In this way you can display directly the cropped image in the final page:
<?php
echo $html->image($uploaded);
?>

Thanks a lot!
Posted Jul 27, 2008 by Germain Guglielmetti