on Tutorials

HTML5 image upload, resize and crop

15 comments
HTML5_Logo_512
Tweet about this on TwitterShare on FacebookShare on Google+Share on LinkedInShare on RedditShare on StumbleUpon

Last week I showed you how to upload images, resize and crop them using PHP. As I promised, this week I will show some modern and advanced HTML5 image upload techniques that you can use on your site.

HTML5 is a big trend and an awesome way of building sexy web applications that your users will just love. HTML5 introduced its File API for representing file objects in web applications, as well as programmaticaly selecting them and accessing their data.

There are two ways of using it:

There are lot of fancy stuff available and I will try to show as many as possible here.

HTML5 image upload – upload multiple files

Lets get our hands dirty and begin some coding. For HTML part, we will reuse the form from the last week and add some features to it:

<form enctype="multipart/form-data" method="post" action="upload.php">
    <div class="row">
      <label for="fileToUpload">Select Files to Upload</label><br />
      <input type="file" name="filesToUpload[]" id="filesToUpload" multiple="multiple" />
      <output id="filesInfo"></output>
    </div>
    <div class="row">
      <input type="submit" value="Upload" />
    </div>
</form>

We are not dealing with fancy styles, so there is no CSS involved. Notice the name of the file input. It allows us to store multiple files as multidimensional array. And notice the multiple attribute which is simply telling the browser to allow multiple files to be selected in it’s browse window.

The real thing here is that the browsers that do not support HTML5 features of FileAPI, will simply ignore multiple attribute and everything will act like in the first tutorial.

JavaScript code for file handling looks like this:

<script>
function fileSelect(evt) {
    var files = evt.target.files;

    var result = '';
    var file;
    for (var i = 0; file = files[i]; i++) {
		result += '<li>' + file.name + ' ' + file.size + ' bytes</li>';
    }
    document.getElementById('filesInfo').innerHTML = '<ul>' + result + '</ul>';
}

document.getElementById('filesToUpload').addEventListener('change', fileSelect, false);
</script>

Try this now, select multiple files and you should see some file info below your form inside output element. Nice.

This is ideal solution, when browser supports everything. Let me show you how to check for support in your browser:

function fileSelect(evt) {
    if (window.File && window.FileReader && window.FileList && window.Blob) {
        var files = evt.target.files;
        var result = '';
        var file;
	for (var i = 0; file = files[i]; i++) {
	    result += '<li>' + file.name + ' ' + file.size + ' bytes</li>';
        }
	document.getElementById('filesInfo').innerHTML = '<ul>' + result + '</ul>';
    } else {
	alert('The File APIs are not fully supported in this browser.');
    }
}
document.getElementById('filesToUpload').addEventListener('change', fileSelect, false);

If you run this code in Internet Explorer, you will be able to select only one file and alert will pop up that the File APIs are not supported.

HTML5 image upload – drag and drop support

This really the interesting part. Some browsers treat input type=”file” like a drop target, so if you try to drag and drop files into a form from previous example in FireFox, it will work out of the box.

HTML for dragging and dropping looks like this:

<div id="dropTarget" style="width: 100%; height: 100px; border: 1px #ccc solid; padding: 10px;">Drop some files here</div>
<output id="filesInfo"></output>

Don’t mind the inline CSS, it is there just to make the drop target bigger. The JavaScript is veyr similar to before:

function fileSelect(evt) {
	evt.stopPropagation();
	evt.preventDefault();
	if (window.File && window.FileReader && window.FileList && window.Blob) {
		var files = evt.dataTransfer.files;

		var result = '';
		var file;
		for (var i = 0; file = files[i]; i++) {
			result += '<li>' + file.name + ' ' + file.size + ' bytes</li>';
		}
		document.getElementById('filesInfo').innerHTML = '<ul>' + result + '</ul>';
	} else {
		alert('The File APIs are not fully supported in this browser.');
	}
}

function dragOver(evt) {
	evt.stopPropagation();
	evt.preventDefault();
	evt.dataTransfer.dropEffect = 'copy';
}

var dropTarget = document.getElementById('dropTarget');
dropTarget.addEventListener('dragover', dragOver, false);
dropTarget.addEventListener('drop', fileSelect, false);

Oh, really nice.

HTML5 image upload – show file preview before upload

There is plenty more stuff you can do with this. I will show you how to show the preview of the image before actual upload happen.

Some HTML:

<form enctype="multipart/form-data" method="post" action="upload.php">
    <div class="row">
      <label for="fileToUpload">Select Files to Upload</label><br />
      <input type="file" name="filesToUpload[]" id="filesToUpload" multiple="multiple" />
	  <output id="filesInfo"></output>
    </div>
    <div class="row">
      <input type="submit" value="Upload" />
    </div>
  </form>

And the JavaScript:

function fileSelect(evt) {
	if (window.File && window.FileReader && window.FileList && window.Blob) {
		var files = evt.target.files;

		var result = '';
		var file;
		for (var i = 0; file = files[i]; i++) {
			// if the file is not an image, continue
			if (!file.type.match('image.*')) {
				continue;
			}

			reader = new FileReader();
			reader.onload = (function (tFile) {
				return function (evt) {
					var div = document.createElement('div');
					div.innerHTML = '<img style="width: 90px;" src="' + evt.target.result + '" />';
					document.getElementById('filesInfo').appendChild(div);
				};
			}(file));
			reader.readAsDataURL(file);
		}
	} else {
		alert('The File APIs are not fully supported in this browser.');
	}
}

document.getElementById('filesToUpload').addEventListener('change', fileSelect, false);

Actual HTML5 image upload

Finally, let’s upload this images to server using XMLHttpRequest:

if (window.File && window.FileReader && window.FileList && window.Blob) {
	document.getElementById('filesToUpload').onchange = function(){
		var xhr = new XMLHttpRequest();
		xhr.onreadystatechange = function(ev){
			document.getElementById('filesInfo').innerHTML = 'Done!';
		};
		xhr.open('POST', 'upload.php', true);
		var files = document.getElementById('filesToUpload').files;
		var data = new FormData();
		for(var i = 0; i < files.length; i++) data.append('file' + i, files[i]);
		xhr.send(data);
	};
} else {
	alert('The File APIs are not fully supported in this browser.');
}

For resizing and cropping these images, we will modify the script from the last week’s tutorial (ImageManipulator class is available here):

// include ImageManipulator class
require_once('ImageManipulator.php');

foreach ($_FILES as $file) {
	// array of valid extensions
	$validExtensions = array('.jpg', '.jpeg', '.gif', '.png');
	// get extension of the uploaded file
	$fileExtension = strrchr($file['name'], ".");
	// check if file Extension is on the list of allowed ones
	if (in_array($fileExtension, $validExtensions)) {
		$newNamePrefix = time() . '_';
		$manipulator = new ImageManipulator($file['tmp_name']);
		$width 	= $manipulator->getWidth();
		$height = $manipulator->getHeight();
		$centreX = round($width / 2);
		$centreY = round($height / 2);
		// our dimensions will be 200x130
		$x1 = $centreX - 100; // 200 / 2
		$y1 = $centreY - 65; // 130 / 2

		$x2 = $centreX + 100; // 200 / 2
		$y2 = $centreY + 65; // 130 / 2

		// center cropping to 200x130
		$newImage = $manipulator->crop($x1, $y1, $x2, $y2);
		// saving file to uploads folder
		$manipulator->save('uploads/' . $newNamePrefix . $file['name']);
		echo 'Done ...';
	} else {
		echo 'You must upload an image...';
	}
}

Just one more thing …

Client size resizing before upload

This is a very neat feature. For example, your user wants to upload a photo directly from the camera. The idea is to resize the image to some normal resolution before uploading and save time uploading it to server.

This can be achieved with canvas and some of the techniques described above. HTML is the same and JavaScript looks like this:

if (window.File && window.FileReader && window.FileList && window.Blob) {
	document.getElementById('filesToUpload').onchange = function(){
		var files = document.getElementById('filesToUpload').files;
		for(var i = 0; i < files.length; i++) {
			resizeAndUpload(files[i]);
		}
	};
} else {
	alert('The File APIs are not fully supported in this browser.');
}

function resizeAndUpload(file) {
var reader = new FileReader();
	reader.onloadend = function() {

	var tempImg = new Image();
	tempImg.src = reader.result;
	tempImg.onload = function() {

		var MAX_WIDTH = 400;
		var MAX_HEIGHT = 300;
		var tempW = tempImg.width;
		var tempH = tempImg.height;
		if (tempW > tempH) {
			if (tempW > MAX_WIDTH) {
			   tempH *= MAX_WIDTH / tempW;
			   tempW = MAX_WIDTH;
			}
		} else {
			if (tempH > MAX_HEIGHT) {
			   tempW *= MAX_HEIGHT / tempH;
			   tempH = MAX_HEIGHT;
			}
		}

		var canvas = document.createElement('canvas');
		canvas.width = tempW;
		canvas.height = tempH;
		var ctx = canvas.getContext("2d");
		ctx.drawImage(this, 0, 0, tempW, tempH);
		var dataURL = canvas.toDataURL("image/jpeg");

		var xhr = new XMLHttpRequest();
		xhr.onreadystatechange = function(ev){
			document.getElementById('filesInfo').innerHTML = 'Done!';
		};

		xhr.open('POST', 'uploadResized.php', true);
		xhr.setRequestHeader("Content-type","application/x-www-form-urlencoded");
		var data = 'image=' + dataURL;
		xhr.send(data);
	  }

   }
   reader.readAsDataURL(file);
}

The code for saving the image on the server is:

if ($_POST) {
    define('UPLOAD_DIR', 'uploads/');
    $img = $_POST['image'];
    $img = str_replace('data:image/jpeg;base64,', '', $img);
    $img = str_replace(' ', '+', $img);
    $data = base64_decode($img);
    $file = UPLOAD_DIR . uniqid() . '.jpg';
    $success = file_put_contents($file, $data);
    print $success ? $file : 'Unable to save the file.';
}

That’s it. I hope you learned something new and will use this knowledge on your next project.

Tweet about this on TwitterShare on FacebookShare on Google+Share on LinkedInShare on RedditShare on StumbleUpon



  • Noel

    Just the thing I was looking for. I didn’t even know you can resize images before uploading, what a cool feature.

    Thanks

  • http://andrewhenderson.me Andrew Henderson

    Excellent tutorial! I love that you used the native code instead of a jQuery. Got to see a lot of the new objects/functions in action! Thanks for taking the time.

  • Pingback: AjaxUpload - Problem mit IE9 - Zend Framework Forum - ZF1 / ZF2

  • http://www.hondonvillas.co.uk Hondon

    Like Noel, I didn’t even know you could re-size images before uploading, what a cool feature as I guess it’s quicker to re-size client side then upload 3MB raw cam images and then do it. Not seen that feature in an uploader before.

    Is there a Demo of this or zip to play with please?

  • tugbm

    How can i add a progress bar or % to the html5 upload? i have all day trying to do that but without results :(

  • Juukie14

    When resizing client side, before uploading, the uploaded image isn’t sharp unfortunately.
    Thanks for the tutorial!

  • donflow

    Great men! Thank You!

  • Pluda

    Hello, first thanks for tut, really nice.

    I’m trying to resize one image teken from my iphone camera and upload it to my webserver. It works, but…

    Lets say I’ve defined var MAX_WIDTH = 320; and var MAX_HEIGHT = 320;

    What is happening is that my image gets srinked inside a 320px black square…

    Do you have any clue on why this is happening?

    Thanks!

    Pluda

    • http://codeforest.net Zvonko Biškup

      I am sorry, but this never happened to me. You are just getting a black square?

      • Pluda

        Hello,

        thanks for reply.

        Not just a black square, I get lets say 10px srinked image and 310px black square.

        this just happens using a mobile device, in my case one iphone 3gs IOS6.1.2, on desktop browser it will work as expected.

        here is the sample

        http://www.mataborrao.eu/teste/index_teste.html

        Thanks

  • Bryan

    may i know where can i get ImageManipulator.php file??
    Thanks :)

    • http://codeforest.net Zvonko Biškup

      Hi there, you can get it here.
      I also updated the tutorial and put this link in the text. Thanks for pointing that out.

  • Fred

    Hello, thanks for the script. i am trying to test the script but it does not work. you call a file named ‘uploadResized.php’. Where is it file. do you have a sample or zip file of your tutorial ? thanks for all

    • http://www.codeforest.net Codeforest

      Hi Fred, the last piece of code from the tutorial is saved in file called uploadResized.php and that’s it…

  • http://www.flatlinemedia.co.uk Bladeski

    This is great! How would I implement this in a form with other inputs, and upload the whole form as one when pressing submit? I am trying to resize the image client side, but when I am submitting my form (with PHP) it submits the original image!