PHP & jQuery image upload and cropMonday, February 11th, 2013

Ajax, Articles, Jquery, PHP

We needed a PHP and jQuery image upload and crop tool and came up with the following. Hope it helps!

Before you start, ensure you have the following:

  • PHP 4 or Higher (It has been tested on Version 5)
  • Safe mode must be off! – A number of users have reported issues when safe mode is on.
  • PHP GD Library
  • jQuery ver 1.2.3 or Higher
  • Image Area Select plugin by Michal Wojciechowski

Our Requirement

What we needed was a way to upload a JPG image, resize it if required then crop it to given height and width.

1. Firstly we created a form to allow us to upload an image. Pretty basic, nothing flashy there.

<form action="my_upload.php" enctype="multipart/form-data" method="post">Photo <input name="image" size="30" type="file" /> <input name="upload" type="submit" value="Upload" /></form>

We will be using the session variable to hold the random file name (in our case the timestamp).
We are now also storing the file extension that is passed through the script, to ensure we are dealing with the right type of image.

//only assign a new timestamp if the session variable is empty
    if (strlen($_SESSION['random_key'])==0){
        $_SESSION['random_key'] = strtotime(date('Y-m-d H:i:s')); //assign the timestamp to the session variable
        $_SESSION['user_file_ext']= "";
    }

2. Capture, rename and resize the uploaded file.

if (isset($_POST["upload"])) {
	//Get the file information
	$userfile_name = $_FILES["image"]["name"];
	$userfile_tmp = $_FILES["image"]["tmp_name"];
	$userfile_size = $_FILES["image"]["size"];
	$filename = basename($_FILES["image"]["name"]);
	$file_ext = substr($filename, strrpos($filename, ".") + 1);

	//Only process if the file is a JPG and below the allowed limit
	if((!empty($_FILES["image"])) && ($_FILES["image"]["error"] == 0)) {
		if (($file_ext!="jpg") && ($userfile_size > $max_file)) {
			$error= "ONLY jpeg images under 1MB are accepted for upload";
		}
	}else{
		$error= "Select a jpeg image for upload";
	}
	//Everything is ok, so we can upload the image.
	if (strlen($error)==0){

		if (isset($_FILES["image"]["name"])){

			move_uploaded_file($userfile_tmp, $large_image_location);
			chmod ($large_image_location, 0777);

			$width = getWidth($large_image_location);
			$height = getHeight($large_image_location);
			//Scale the image if it is greater than the width set above
			if ($width > $max_width){
				$scale = $max_width/$width;
				$uploaded = resizeImage($large_image_location,$width,$height,$scale);
			}else{
				$scale = 1;
				$uploaded = resizeImage($large_image_location,$width,$height,$scale);
			}
			//Delete the thumbnail file so the user can create a new one
			if (file_exists($thumb_image_location)) {
				unlink($thumb_image_location);
			}
		}
		//Refresh the page to show the new uploaded image
		header("location:".$_SERVER["PHP_SELF"]);
		exit();
	}
}

The validation section has also been updated and been made more secure by checking the mime type as well as the image extension.

foreach ($allowed_image_types as $mime_type => $ext) {
        //loop through the specified image types and if they match the extension then break out
	//everything is ok so go and check file size
	if($file_ext==$ext && $userfile_type==$mime_type){
		$error = "";
		break;
	}else{
		$error = "Only <strong>".$image_ext."</strong> images accepted for upload";
	}
}
//check if the file size is above the allowed limit
if ($userfile_size > ($max_file*1048576)) {
	$error.= "Images must be under ".$max_file."MB in size";
}

3. Now that the image has been uploaded and saved to our folder we can use the “Image Area Select” plugin to crop our image.

It basically provides the coordinates to the server to handle the crop.

  • x1, y1 = coordinates of the top left corner of the initial selection area
  • x2, y2 = coordinates of the bottom right corner of the initial selection area
  • width = crop selection width
  • height = crop selection height

There are a number of options with this plugin which you can see using the link above. We opted for an aspect ratio of 1:1 (height and width of 100px) along with a preview of what we are actually going to crop.

Lets break it down, we first set the imgAreaSelect function to the image we want to crop, i.e. the one we just uploaded. As you can see, the aspect ration is set to 1:1, which means we are going to get a square selection. The “onSelectChange” is a callback function which runs the preview function when a change is made to the crop.

Update: Using the php calculation below makes the script that much more dynamic, all you now have to do is set the height and width of your thumbnail and the script will calculate the ratio/preview sizes for you!

$(window).load(function () {
    $("#thumbnail").imgAreaSelect({ aspectRatio: "1:thumb_height/thumb_width", onSelectChange: preview });
});

The preview function below, is run as soon as you create your selection. This places the right part of the image into the preview window. The second part of the function populates hidden fields which are later passed to the server.

function preview(img, selection) {
	var scaleX = 100 / selection.width;
	var scaleY = 100 / selection.height; 

	$("#thumbnail + div > img").css({
		width: Math.round(scaleX * 200) + "px",
		height: Math.round(scaleY * 300) + "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);
}

This function is not really required, but helps by checking to see if the user has made a crop selection. In our case it is a required step. The form is submitted if the values exist, i.e. a selection has been made.

$(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("You must make a selection first");
			return false;
		}else{
			return true;
		}
	});
});

4. It’s time to handle these new coordinates and generate our new cropped thumbnail.

if (isset($_POST["upload_thumbnail"])) {
	//Get the new coordinates to crop the image.
	$x1 = $_POST["x1"];
	$y1 = $_POST["y1"];
	$x2 = $_POST["x2"]; // not really required
	$y2 = $_POST["y2"]; // not really required
	$w = $_POST["w"];
	$h = $_POST["h"];
	//Scale the image to the 100px by 100px
	$scale = 100/$w;
	$cropped = resizeThumbnailImage($thumb_image_location, $large_image_location,$w,$h,$x1,$y1,$scale);
	//Reload the page again to view the thumbnail
	header("location:".$_SERVER["PHP_SELF"]);
	exit();
}

That is pretty much it, and all it took was 2 javascript files! Take a look at the demo and download a copy to see the full working code.

So far we have tested it in FireFox v2, Internet Explorer 6 and 7 with pretty good results. (Let us know if you find any issues with other browsers.)

Change Log

  • v.1.2
    • Upload different types of images, JPG, GIFs and PNG
    • The image upload error check has been fixed (thanks to DevWooty)
  • v.1.1
    • Upload images and have a random file name (this fixes the caching issue some of you have had)
    • Error check
  • v.1.0
    • Initial plugin

Like it? Share it!

Tagged with:

Amit Thakrar

Amit is a web designer and developer with a passion for design, interaction and usability. In his free time if he's not at the gym he's traveling the world.

View all posts by: Amit Thakrar

Follow us on Google+

Feedback (268)

Leave a Comment
  1. HIMANSHU says:

    I want to generate thumbnail what user select cropping area so it’s not give perfect result when i upload more than 2000px width image.

  2. Indra says:

    It is not working in firefox after a few attempts, but its working in chrome and IE. I tried delete cookies and clear cache. But not working in Firefox .

  3. Vapes says:

    Great Information sir.
    thanks

  4. ketan says:

    oh…thanks a lot…..excellent brother……..this is very helpful for me…..

  5. Michal Matusik says:

    How to set default crop size? For example, I’m need thumbnails 240×90 and any another size.

  6. das says:

    hi.. v1.2 worked properly but after 1hour i tried it again. It wont works please solve this issue…

  7. Sanath says:

    I need to change thumbnail with and height when select main image. May I know how to do that? I don’t need fix thumbnail size.

  8. ver 1.2 not working in firefox

  9. Prateek says:

    There are some tutorials out there for the same. But this was well described and simple. Thanks, it really helped.

  10. Danny DePralue says:

    What a magnificent script, congratulations, works just great!!!

  11. Jürgen Meier says:

    Any idea how to realize a free definable rectangle (via mouse) instead of a fixed aspect ratio?

  12. Arun Srivastav says:

    Could please any buddy make code for me once I have saved image if after some time I again call the save image and reedit it with crop function and again want to save it how it would be possible?

  13. Grant says:

    Excellent script. Works great except that I cannot get it to work in Firefox. I can get the image, show the large and thumbnail images, but cannot draw the box over the large image to make the thumbnail selection.

    Anyone come across this and if yes, can you share the solution.

    Thanks
    Grant

  14. Grant says:

    Am I wrong on this. It looks like the script takes the tmp file and uploads it to the large file location and also to the thumbnail file location. Once the image is cropped, then the thumbnail file is updated using the chmod command to reflect what the user wanted cropped. Is it possible to create a tmp file for the cropped image and only after accepted write it to a permanent file.

  15. PavanMaganti says:

    It’s very Useful code.But I’ve changed the directory path to C:upload_pic its not working properly. Can u give any idea?

  16. Hungry-Lord says:

    Very useful indeed. Thanks a lot for sharing

  17. German says:

    Hello! great script so far. Is there any way to display right away after you upload the image the dotted square that you are supposed to move around and resize to create the cropped image? It only shows up if you actually drag inside the image. There are lots of people that would be like “now what should i do and why it says i cant save the file” .. So if they see that dotted square right away at least it gives them a “hint” of what they have to do…. Anybody knows how to make it show up right away without having to drag around the mouse inside the original uploaded file? Thanks in advance

  18. webmaster says:

    Is there a way to like assign a different directory? Something like

    / public_html / root / new_folder_instead_of_default_folder / test /

    And is there a way to assign an actual file name to the uploaded file?

    such as “123.jpg”

    And
    if “123.jpg” exist then allow a new upload or a crop of the image. If
    “123.jpg” doesn’t exist, then allow upload. I’ve tried to do these 2
    things, but I keep getting errors.

  19. someone says:

    what about transparent parts will be transparent after cropping too?

  20. Sergio Urra says:

    Hi. Many thanks for sharing.

    I’m moving to the new version and I’d like to know if it’s possible to alter the code for:

    -to keep the original name of the image (prefix+originalName) instead the session ID.
    -to create not one but two thumbs with two different sizes

    Thanks

  21. Sergio Urra says:

    I have had this problem:

    Action: Images selected for upload and crop

    Message type: “no files selected” (spanish version)

    Result: Image stored in “upload_pics”, but the crop system doesn’t appears.

    Browser: Google Chrome Versión 28.0.1500.72 m

    FIX: delete the cookie (I did use the extension “Edit This Cookie 1.2.1″ for Chrome)

    Seems there is a problem with the “Delete Images” rutine (see below message by Velvet for a solution)

    Hope this help someone.

    Cheers

    • Eve Mota says:

      I have the same problem with firefox 24.0 and safari!

      I tried the two solutions you explain, but it doesn’t work.

      Actually, I have two problems: crop system doesn’t appear

      and the image is stored as “something”.jpg.jpg

      For the second issue I read I have to empty cache. But I have to do it every time and it’s not practical…

      Anyone have an idea? Please help me!!

  22. lance says:

    hi. such a nice script. just wanna ask how to add a default selection right away after uploading the image? thanks

  23. Evan says:

    Hello and thank you very much. I was wondering if there is a way to have the preview image dynamically size to the crop window? I dont want to have to set oth the height and width of the preview image before the user selects what they want to crop. Is this possible?

  24. Timo Veld says:

    Hi,

    Nice script. Is there a way method to upload the image and resize the preview without overwriting it in a lower resolution?

    I’ve tried using max-width in CSS, but then the X and Y handlers no longer correspond correctly.

    Basically what I’m after is the same as the boxWidth property that Jcrop uses.

  25. Timo Veld says:

    Is there a way to disable the resizing of the uploaded file but still giving a smaller window in which to crop the image. Raising the following variable: $max_width , allows me to do the first, the second is a little trickier. I tried giving the images a style with a max-width. But that messed up the preview.

    I’m basically trying to create the normal functionality, only without losing the high resolution.

    Any ideas on how to achieve this?

    • Karl Guven says:

      Hi Timo, have you found an answer for this yet!? I am also trying to figure out a way to make the visible full size image being cropped smaller, but only visibly on the screen, without actually resizing the image to a smaller width height.

      I tried setting width and height to the image being cropped but then when i crop, the thumbnail doesnt match the area i selected to crop. please, if anyone can offer any help i would greatly appreciate it!!

  26. rakesh says:

    I love this plugin but the problem I have had is file automatically adding the extension twice.

    For example I end up with resize_0123456789.jpg.jpg this means the image then doesn’t get displayed for the user to select a thumbnail area.

    Does anyone have any ideas why this could be happening?

    Thanks again for a great plugin

    • zeeked says:

      I have the same problem too … has anybody found a solution for that? regards :)

    • Gino says:

      You have to remove the added extension on these lines:

      $large_image_location = $large_image_location. "." .$file_ext;
      $thumb_image_location = $thumb_image_location. "." .$file_ext;

      It should be:

      $large_image_location = $large_image_location;
      $thumb_image_location = $thumb_image_location;

      :))

    • Leonnerd says:

      There is a huge bug in this application around the extension. When the page loads the first time the extension is blank (“”). Now the line:

      $large_image_location = $upload_path. $large_image_name. $_SESSION['user_file_ext'];

      sets the image to have no extension. So the image name will be resize_21357153 (no extension). Then when you start saving the image there is a line:

      $large_image_location = $large_image_location;

      This will save the image without extension and your application will fail to load the image on refresh because it is looking for an image with extension.

      The solution to the problem is to change:

      $large_image_location = $large_image_location;

      to

      $large_image_location = $upload_path. $large_image_name. "." .$file_ext;

  27. charanjeet singh says:

    i want to name output thumbnails according to value stored in variable in $gall instead of session keys. how can i do it?


Leave a Comment

You can use these HTML tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>