jQuery image upload & crop
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:
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
- 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:
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($filename, strrpos($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($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);
imagejpeg($newImage,$thumb_image_name,90);
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:
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:".$tW."px; height:".$tH."px;\">
<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"], 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
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
Comment
1 Small corrections and adjustments
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");
}
Comment
2 really nice thing but not quite working
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?
Comment
3 Hmm
Thanks to Eskil and Mark.
Comment
4 Very cool script
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!
Comment
5 Problem with session !
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
Bug
6 fix for the thumbnail preview div
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;\">
Comment
7 thanks
i edited it.
Question
8 hi i got a problem...
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...?
Comment
9 I think you missed something..
echo $cropimage->createJavaScript($uploaded['imageWidth'],$uploaded['imageHeight'],151,151);Comment
10 Bug in IE*?
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.