Ajax Image Upload and Resize with jQuery and PHP

There are plenty of great image uploader scripts on the net, but you may find them complicated to implement, especially if you are novice one. Those uploader(s) come with additional scripts and files which you may not even need, so sometimes your best bet is to code your own image upload and resize script, which will serve the purpose and keep things simple.
  • 5.2.0+
  • 1.6.1+
Today we are going to create an Ajax based image upload and resize script, which means the image file will be uploaded to server using Ajax request. We will also be using HTML5 File API to check file size and image type before uploading, then with PHP support we will create two images, a resized version and a thumbnail from original image file. The examples you’ll find here are pretty easy to understand. Ajax Image Uploader

HTML Image Upload Form

In HTML form, we need one file input field as shown in code below, notice the multiple=”multiple” attribute in input field, it let’s us select multiple files, you may remove it if you only accept single file.
HTML
1234567
<div class="form-wrap">
    <form action="process.php" method="post" enctype="multipart/form-data" id="upload_form">
        <input name="__files[]" type="file" multiple="multiple" />
        <input name="__submit__" type="submit" value="Upload"/>
    </form>
    <div id="output"><!-- error or success results --></div>
</div>

jQuery

Thanks to HTML5 for new File API, we can now do things like checking file size, type etc. But we also want to make sure client browser supports this feature. Most modern browsers comes with File API support, but for old browsers (like IE8), we can display a nice message like so:
JQUERY
12345
$("#upload_form").on( "submit", function(event) {//on form submit
    if(!window.File && window.FileReader && window.FileList && window.Blob){ //if browser doesn't supports File API
        alert("Your browser does not support new File API! Please upgrade.");
    }
})

Checking file size/type with HTML5 File API

Once we know the browser supports File API, we can start taking things further. Before we send our files directly to server, we want to make sure, file size is not too big and it is an image file. We also want to limit number of files user can upload. Here’s the example jQuery code :
JQUERY
12345678910111213141516171819202122232425262728293031323334
$("#upload_form").on( "submit", function(event) {//on form submit
    var proceed = true; //set proceed flag
    if(!window.File && window.FileReader && window.FileList && window.Blob){ //if browser doesn't supports File API
        alert("Your browser does not support new File API! Please upgrade.");
		proceed = false;
    }else{

		//Limit Files Selection
		var total_selected_files = this.elements['__files[]'].files.length; //number of files selected
		if(total_selected_files > 3){ //limit number of files allowed to 3
			alert( "You have selected "+total_selected_files+" file(s), 3 is maximum!"); //notify user
			proceed = false;
		}
		
		//iterate files in file input field
		var total_files_size = 0;
		$(this.elements['__files[]'].files).each(function(i, ifile){
			if(ifile.value !== ""){ //continue only if file(s) are selected
				if(['image/png', 'image/gif', 'image/jpeg', 'image/pjpeg'].indexOf(ifile.type) === -1){ //check unsupported file
					alert( "<b>"+ ifile.name + "</b> is unsupported file type!");
					proceed = false;
				}
				total_files_size = total_files_size + ifile.size; //add file size to total size
			}
		});	
		
		//if total file size is greater than max file size
		if(total_files_size > 1048576){ 
			alert( "Make sure total file size is less than 1 MB!");
			proceed = false;
		}

	}
})
Now let’s put things together, this time we also use jQuery Ajax. As you can see in code below, we check file size, type and limit the selection of multiple files, then if everything looks good, we create FormData object new FormData() which will be sent to server using jQuery Ajax.
JQUERY
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273
//configuration
var max_file_size 			= 2048576; //allowed file size. (1 MB = 1048576)
var allowed_file_types 		= ['image/png', 'image/gif', 'image/jpeg', 'image/pjpeg']; //allowed file types
var result_output 			= '#output'; //ID of an element for response output
var my_form_id 				= '#upload_form'; //ID of an element for response output
var total_files_allowed 	= 3; //Number files allowed to upload

//on form submit
$(my_form_id).on( "submit", function(event) { 
	event.preventDefault();
	var proceed = true; //set proceed flag
	var error = [];	//errors
	var total_files_size = 0;
	
	if(!window.File && window.FileReader && window.FileList && window.Blob){ //if browser doesn't supports File API
		error.push("Your browser does not support new File API! Please upgrade."); //push error text
	}else{
		var total_selected_files = this.elements['__files[]'].files.length; //number of files
		
		//limit number of files allowed
		if(total_selected_files > total_files_allowed){
			error.push( "You have selected "+total_selected_files+" file(s), " + total_files_allowed +" is maximum!"); //push error text
			proceed = false; //set proceed flag to false
		}
		 //iterate files in file input field
		$(this.elements['__files[]'].files).each(function(i, ifile){
			if(ifile.value !== ""){ //continue only if file(s) are selected
				if(allowed_file_types.indexOf(ifile.type) === -1){ //check unsupported file
					error.push( "<b>"+ ifile.name + "</b> is unsupported file type!"); //push error text
					proceed = false; //set proceed flag to false
				}

				total_files_size = total_files_size + ifile.size; //add file size to total size
			}
		});
		
		//if total file size is greater than max file size
		if(total_files_size > max_file_size){ 
			error.push( "You have "+total_selected_files+" file(s) with total size "+total_files_size+", Allowed size is " + max_file_size +", Try smaller file!"); //push error text
			proceed = false; //set proceed flag to false
		}
		
		var submit_btn  = $(this).find("input[type=submit]"); //form submit button	
		
		//if everything looks good, proceed with jQuery Ajax
		if(proceed){
			submit_btn.val("Please Wait...").prop( "disabled", true); //disable submit button
			var form_data = new FormData(this); //Creates new FormData object
			var post_url = $(this).attr("action"); //get action URL of form
			
			//jQuery Ajax to Post form data
			$.ajax({
				url : post_url,
				type: "POST",
				data : form_data,
				contentType: false,
				cache: false,
				processData:false,
				mimeType:"multipart/form-data"
			}).done(function(res){ //
				$(my_form_id)[0].reset(); //reset form
				$(result_output).html(res); //output response from server
				submit_btn.val("Upload").prop( "disabled", false); //enable submit button once ajax is done
			});
		}
	}
	
	$(result_output).html(""); //reset output 
	$(error).each(function(i){ //output any error to output element
		$(result_output).append('<div class="error">'+error[i]+"</div>");
	});
		
});

Processing Image

The backbone of this uploader is this PHP file, this is where uploaded image files are resized and saved, then outputted back to browser. Below is the working PHP code which can handle multiple image files. You can go through the it and understand how this code really works. But if you still find it bit complicated, don’t worry I’ve converted following code in a separate PHP class in download section, which you can include in your project, play with it or even contribute if you can improve the code.
PHP
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
<?php
############ Configuration ##############
$config["image_max_size"] 				= 500; //Maximum image size (height and width)
$config["thumbnail_size"]  				= 200; //Thumbnails will be cropped to 200x200 pixels
$config["thumbnail_prefix"]				= "thumb_"; //Normal thumb Prefix
$config["destination_folder"]			= 'home/Website/ajax-image-upload/uploads/'; //upload directory ends with / (slash)
$config["thumbnail_destination_folder"]	= 'home/Website/ajax-image-upload/uploads/'; //upload directory ends with / (slash)
$config["upload_url"] 					= "http://website/ajax-image-upload/uploads/"; 
$config["quality"] 						= 90; //jpeg quality


if(!isset($_SERVER['HTTP_X_REQUESTED_WITH'])) {
	exit;  //try detect AJAX request, simply exist if no Ajax
}

if(!isset($_FILES['__files']) || !is_uploaded_file($_FILES['__files']['tmp_name'][0])){
   die('Image file is Missing!');
}

//count total files in array
$file_count = count($_FILES["__files"]["name"]);

if($file_count > 0){ //there are more than one file? no problem let's handle multiple files

	for ($x = 0; $x < $file_count; $x++){	//Loop through each uploaded file
	
		//if there's file error, display it
		if ($_FILES["__files"]['error'][$x] > 0) { 
			print get_upload_error($x);
			exit;
		}

		//Get image info from a valid image file
		$im_info = getimagesize($_FILES["__files"]["tmp_name"][$x]);
		if($im_info){
			$im["image_width"]	= $im_info[0]; //image width
			$im["image_height"]	= $im_info[1]; //image height
			$im["image_type"]	= $im_info['mime']; //image type
		}else{
			die("Make sure image <b>".$_FILES["__files"]["name"][$x]."</b> is valid image file!");
		}
		
		//create image resource using Image type and set the file extension
		switch($im["image_type"]){
			case 'image/png':
				$img_res =  imagecreatefrompng($_FILES["__files"]["tmp_name"][$x]);
				$file_extension = ".png";
				break;
			case 'image/gif':
			   $img_res = imagecreatefromgif($_FILES["__files"]["tmp_name"][$x]);     
			   $file_extension = ".gif";
			   break;
			case 'image/jpeg': 
			case 'image/pjpeg':
				$img_res = imagecreatefromjpeg($_FILES["__files"]["tmp_name"][$x]);
				$file_extension = ".jpg";
				break;
			default:
				$img_res = 0;
        }
		
		//set our file variables 
		$unique_id =  uniqid(); //unique id for random filename
		$new_file_name = $unique_id . $file_extension; 
		$destination_file_save = $config["destination_folder"] . $new_file_name; //file path to destination folder
		$destination_thumbnail_save = $config["thumbnail_destination_folder"] . $config["thumbnail_prefix"]. $new_file_name; //file path to destination thumb folder

		if($img_res){
			###### resize Image ########
			//Construct a proportional size of new image
			$image_scale    = min($config["image_max_size"]/$im["image_width"], $config["image_max_size"]/$im["image_height"]);
			$new_width      = ceil($image_scale * $im["image_width"]);
			$new_height     = ceil($image_scale * $im["image_height"]);
	
			//Create a new true color image
			$canvas  = imagecreatetruecolor($new_width, $new_height);
			$resample = imagecopyresampled($canvas, $img_res, 0, 0, 0, 0, $new_width, $new_height, $im["image_width"], $im["image_height"]);
			if($resample){
				$save_image = save_image_file($im["image_type"], $canvas, $destination_file_save, $config["quality"]); //save image
				if($save_image){
					print '<img src="'.$config["upload_url"] . $new_file_name. '" />'; //output image to browser
				}
			}
			
			if(is_resource($canvas)){ 
			  imagedestroy($canvas);  //free any associated memory 
			} 

			
			###### Generate Thumbnail ########
			
			//Offsets 
			if( $im["image_width"] > $im["image_height"]){
				$y_offset = 0;
				$x_offset = ($im["image_width"] - $im["image_height"]) / 2;
				$s_size     = $im["image_width"] - ($x_offset * 2);
			}else{
				$x_offset = 0;
				$y_offset = ($im["image_height"] - $im["image_width"]) / 2;
				$s_size = $im["image_height"] - ($y_offset * 2);
			}
			
		 	//Create a new true color image
			$canvas = imagecreatetruecolor($config["thumbnail_size"], $config["thumbnail_size"]); 
			$resample = imagecopyresampled($canvas, $img_res, 0, 0, $x_offset, $y_offset, $config["thumbnail_size"], $config["thumbnail_size"], $s_size, $s_size);
			if($resample){
				$save_image = save_image_file($im["image_type"], $canvas, $destination_thumbnail_save, $config["quality"] );
				if($save_image){
					print '<img src="'.$config["upload_url"] . $config["thumbnail_prefix"]. $new_file_name. '" />';
				}
			}
			
			if(is_resource($canvas)){ 
			  imagedestroy($canvas);  //free any associated memory 
			} 
			
			
		}
		
	}
}
 
 //funcion to save image file
function save_image_file($image_type, $canvas, $destination, $quality){
	switch(strtolower($image_type)){
		case 'image/png': 
			return imagepng($canvas, $destination); //save png file
		case 'image/gif': 
			return imagegif($canvas, $destination); //save gif file				   
		case 'image/jpeg': case 'image/pjpeg': 
			return imagejpeg($canvas, $destination, $quality);  //save jpeg file
		default: 
			return false;
	}
}

function get_upload_error($err_no){
	switch($err_no){
		case 1 : return 'The uploaded file exceeds the upload_max_filesize directive in php.ini.';
		case 2 : return 'The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form.';
		case 3 : return 'The uploaded file was only partially uploaded.';
		case 4 : return 'No file was uploaded.';
		case 5 : return 'Missing a temporary folder. Introduced in PHP 5.0.3';
		case 6 : return 'Failed to write file to disk. Introduced in PHP 5.1.0';
	}
}
That’s all you need to create an Ajax based image upload and resize script. The downloadable file contains sample script. Hope it will help you create interesting upload form for your website.
Next :Add Progressbar to your Upload form.
Ajax Image Upload and Resize with ImageMagick and Ajax File Upload (Progressbar) Good luck!