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:

By using <input type=”file”> and its change event

By dragging and dropping files from your computer directly in the browser

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.