jQuery image upload & crop
I was searching for a nice script with that i can upload and crop images.
I found a nice one @ [url]http://www.webmotionuk.co.uk/php-jquery-image-upload-and-crop/[/url] and made it cake ready ;)
So, i hope i can help you with my first Article!
Here we go:
Requirements
- PHP 4 or Higher (It has been tested on Version 5)
- PHP GD Library
- jQueryhttp://jquery.com/
- Image Area Select plugin -> http://odyniec.net/projects/imgareaselect/
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:
<?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($filename, strrpos($filename, ".") + 1);
$uploadTarget = $upload_path.$filename;
if(empty($uploadedInfo)) {
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);
if($ext == "png" || $ext == "PNG"){
imagepng($newImage,$image,0);
}elseif($ext == "jpg" || $ext == "jpeg" || $ext == "JPG" || $ext == "JPEG"){
imagejpeg($newImage,$image,90);
}elseif($ext == "gif" || $ext == "GIF"){
imagegif($newImage,$image);
}
chmod($image, 0777);
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);
if($ext == "png" || $ext == "PNG"){
imagepng($newImage,$thumb_image_name,0);
}elseif($ext == "jpg" || $ext == "jpeg" || $ext == "JPG" || $ext == "JPEG"){
imagejpeg($newImage,$thumb_image_name,90);
}elseif($ext == "gif" || $ext == "GIF"){
imagegif($newImage,$thumb_image_name);
}
chmod($thumb_image_name, 0777);
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:
<?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 = $thumbH / 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));
$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:".$tW."px; height:".$tH."px;\">
$imgTumPrev
</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 ( :) )
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:
<?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:
$uploaded = $this->JqImgcrop->uploadImage($this->data['YourModel']['image'], '/img/upload/', 'prefix_');
and then push it to the view:
$this->set('uploaded',$uploaded);
This is what it looks in my controller:
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:
<?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 and add the javascript using:
echo $cropimage->createJavaScript($uploaded['imageWidth'],$uploaded['imageHeight'],151,151);
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"
echo $form->submit('Done', array("id"=>"save_thumb"));
In my case it looks like this:
<?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"], 151, 151);
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
$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:
$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!

var $helpers = array('Html', 'Javascript', 'Form', 'Session');
Otherwise you will experience issues with any sessions you use.
But it's not working in IE8.
Did any one notice that.
Although i manage to solve the problem.
The function createimage_step2 is missing the final }. It should read
function createimage_step2(){
if (!empty($this->data)) {
$uploaded = $this->JqImgcrop->uploadImage($this->data['YourModel']['image'], '/img/upload/', 'prefix_');
$this->set('uploaded',$uploaded);
}
}
You must also create a directory for the images to be uploaded. With the code above, it would be in : ./app/webroot/img/upload
Don't forget to include imgareaselect-default.css in your layout.cpt.
This tutorial is great, but it would have been more helpful if all the code was listed under its file name and not a mix of randomly placed code bits and full contents of code files.
The example form for createimage_step2.ctp is missing the createJavaScript line of code. It should be:
create('YourModel', array('action' => 'createimage_step3',"enctype" => "multipart/form-data"));
echo $form->input('id');
echo $form->hidden('name');
echo $cropimage->createJavaScript($uploaded['imageWidth'],$uploaded['imageHeight'],151,151);
echo $cropimage->createForm($uploaded["imagePath"], 151, 151);
echo $form->submit('Done', array("id"=>"save_thumb"));
echo $form->end();?>
http://fairuzsulaiman.com/2011/05/cakephp-1-3-tutorial-muat-naik-crop-imej/
I presume you need a view called createimage_step1.ctp, but I'm just guessing here.
Help would be great.
- The crop function works, but it doesn't show the rectangle. I'm sure this has to do with the fact that the css files of imgareaselect aren't being loaded. It's unclear to me where to put the css files.
- How can I store both the original image AND the created thumbnail?
Thanks for the help!
I've implemented the cropping to cope with a 16:9 aspect ratio which i've done by changing the aspect ratio within
$('#thumbnail').imgAreaSelect({ aspectRatio: '16:9', onSelectChange: preview });
whilst this changes the dimensions of the cropping frame and saves the images in the right dimensions, it isnt previewing correctly in small thumbnail.
I can see that i can change the height and width of the thumbnail view in
echo $cropimage->createForm($uploaded["imagePath"], 151, 151);
which doesnt seem to do anything other than affect the display of the small thumbnail. Any tips to get this to 16:9 ratio as well so it reflects what i see in the cropping frame?
$('#thumbnail').imgAreaSelect({ aspectRatio: '16:9', onSelectChange: preview });
whilst this changes the dimensions of the cropping frame and saves the images in the right dimensions, it isnt previewing correctly in small thumbnail.
[end quote]
Use this line instead:
$('#thumbnail').imgAreaSelect({ aspectRatio: $thumbW + ':' + $thumbH, onSelectChange: preview });
I'm very new in cakePhp I'm implementing this component into my website on the controller as component but the session is disabled after adding it into my components array.
I don't know what the problem and why its disabled my session.
I just wont access my session on the all actions of that controller.
Anyone please suggest what to do?
Thanks in advanced.
I am new to cakephp. But I didn't have any issues in getting this to work. Thanks for posting.
I would like to point out a minor javascript thing. As is the image in the thumbnail preview is slightly different than what is selected.
The error is in the following line in the create form method of the Helper
$imgTumPrev = $this->Html->image($imagePath, array('style'=>'position: relative;', 'id'=>'thumbnail', 'alt'=>'Thumbnail Preview'));
In this, remove- 'style'=>'position: relative;'
Thanks
[quote] Hi,
I am new to cakephp. But I didn't have any issues in getting this to work. Thanks for posting.
I would like to point out a minor javascript thing. As is the image in the thumbnail preview is slightly different than what is selected.
The error is in the following line in the create form method of the Helper
$imgTumPrev = $this->Html->image($imagePath, array('style'=>'position: relative;', 'id'=>'thumbnail', 'alt'=>'Thumbnail Preview'));
In this, remove- 'style'=>'position: relative;'
Thanks
[end quote]
Warning (2): Cannot modify header information - headers already sent by (output started at C:\xampp\htdocs\teste\app\controllers\components\jq_imgcrop.php:1) [CORE\cake\libs\controller\controller.
I am new to cake and wanted to use this function on my site, can someone help me?
Hug!
I edited this article and inserted your mentions.
@Sergey. Does the IE Fixes also work with older jquery Versions?
Thanks & Cheers,
Thomas
this path. "echo $cropimage->createForm($uploaded["imagePath"], 151, 151);" where the bug is 'Undefined variable: uploaded' in ctp file
(ii) My corp function not work.
Sorry about my english. I am using cakephp1.3.8
1) READ FIXES ABOVE!
2) add id=\"thumbnail_preview\" to image inside DIV in 'createForm' function in helper class.
3) use
$('#thumbnail_preview').css({
instead of
$('#thumbnail + div > img').css({
in helper class.
(this class works fine for me with jquery-1.4.2.min.js and jquery.imgareaselect-0.9.2.min.js)
If you have issues with saving png or gif files (if you see just black image), you should correct few lines in jq_imgcrop.php.
In function resizeImage right after imagecopyresampled, instead of 'imagejpeg($newImage,$image,90);' use following code:
if($ext == "png" || $ext == "PNG"){
imagepng($newImage,$image,0);
}elseif($ext == "jpg" || $ext == "jpeg" || $ext == "JPG" || $ext == "JPEG"){
imagejpeg($newImage,$image,90);
}elseif($ext == "gif" || $ext == "GIF"){
imagegif($newImage,$image);
}
And for 'function resizeThumbnailImage' after 'imagecopyresampled', instead of 'imagejpeg($newImage,$image,90);' use:
if($ext == "png" || $ext == "PNG"){
imagepng($newImage,$image,0);
}elseif($ext == "jpg" || $ext == "jpeg" || $ext == "JPG" || $ext == "JPEG"){
imagejpeg($newImage,$image,90);
}elseif($ext == "gif" || $ext == "GIF"){
imagegif($newImage,$image);
}
Thank you, nice work!
Also, if you start using an aspect ratio other than 1:1, there's a typo in the same helper which is easily fixed:
var scaleX = {$thumbW} / selection.width;
var scaleY = {$thumbH} / selection.height; //This was $thumbW
Otherwise, it works quite well - cheers!
And can you upload an image crop it but keep the original? So you end up with 2 images.
Thanks,
Dave
Everything was working fine in Firefox, Google Chrome etal. But I was having problems with IE (of all flavours). No selection tool would be presented and a javascript warning was produced which told me about an 'Invalid argument' being submitted to the Math.round function.
The cause was that when you first click on the image to start your selection, the scaleX and scaleY variables in the javascript on the page result in a value of Infinity. Firefox and every other browser seems to silently step over this and carry on processing as normal. IE of course did not.
The solution was to add the following line after the initial scaleX and scaleY variables are calculated. This appears to have solved the problem fully.
if(scaleX == Infinity || scaleY == Infinity) return false;I hope this helps someone else and saves them the hour of hunting it cost me ;o)
other than that, awesome plugin and it works really well.
and tried to bake the the image uploader on ubuntu
the thing is i pass throgh the first step when i get to step 2 the script dos show me the image I upload to corp.
but know it dosent corp.
Some one has a clue...?
echo $cropimage->createJavaScript($uploaded['imageWidth'],$uploaded['imageHeight'],151,151);
to<div style=\"position:relative; overflow:hidden; width:151px; height:151px;\">
if you want your thumbnail height/width honored in the preview div.<div style=\"position:relative; overflow:hidden; width:".$tW."px; height:".$tH."px;\">
i edited it.
I got a social network, and the users can create projects... on creation, users can crop an image that represent the respective project so... heres my problem when I load the UserProjectsController it loads:
var $helpers = array('Cropimage');
var $components = array('JqImgcrop');
The two components loads but my app isn't able to read the Session anymore...
I need the user_id that is in the session to be able to copy the uploaded image in the correct folder on the server(localhost for now)...
Anyone did get the same problem ?!?
thks a lot for any help
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:
<?phpecho $html->image($uploaded);
?>
Thanks a lot!
Thanks to Eskil and Mark.
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?
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:
should beecho $form->input('image',"type" => "file");
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");
}