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.
Table of Contents
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:
[code lang=”html”]
&amp;lt;form enctype="multipart/form-data" method="post" action="upload.php"&amp;gt;
&amp;lt;div class="row"&amp;gt;
&amp;lt;label for="fileToUpload"&amp;gt;Select Files to Upload&amp;lt;/label&amp;gt;&amp;lt;br /&amp;gt;
&amp;lt;input type="file" name="filesToUpload[]" id="filesToUpload" multiple="multiple" /&amp;gt;
&amp;lt;output id="filesInfo"&amp;gt;&amp;lt;/output&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class="row"&amp;gt;
&amp;lt;input type="submit" value="Upload" /&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/form&amp;gt;
[/code]
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:
[code lang=”javascript”]
&amp;lt;script&amp;gt;
function fileSelect(evt) {
var files = evt.target.files;
var result = ”;
var file;
for (var i = 0; file = files[i]; i++) {
result += ‘&amp;lt;li&amp;gt;’ + file.name + ‘ ‘ + file.size + ‘ bytes&amp;lt;/li&amp;gt;’;
}
document.getElementById(‘filesInfo’).innerHTML = ‘&amp;lt;ul&amp;gt;’ + result + ‘&amp;lt;/ul&amp;gt;’;
}
document.getElementById(‘filesToUpload’).addEventListener(‘change’, fileSelect, false);
&amp;lt;/script&amp;gt;
[/code]
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:
[code lang=”javascript”]
function fileSelect(evt) {
if (window.File &amp;amp;&amp;amp; window.FileReader &amp;amp;&amp;amp; window.FileList &amp;amp;&amp;amp; window.Blob) {
var files = evt.target.files;
var result = ”;
var file;
for (var i = 0; file = files[i]; i++) {
result += ‘&amp;lt;li&amp;gt;’ + file.name + ‘ ‘ + file.size + ‘ bytes&amp;lt;/li&amp;gt;’;
}
document.getElementById(‘filesInfo’).innerHTML = ‘&amp;lt;ul&amp;gt;’ + result + ‘&amp;lt;/ul&amp;gt;’;
} else {
alert(‘The File APIs are not fully supported in this browser.’);
}
}
document.getElementById(‘filesToUpload’).addEventListener(‘change’, fileSelect, false);
[/code]
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:
[code lang=”html”]
&amp;lt;div id="dropTarget" style="width: 100%; height: 100px; border: 1px #ccc solid; padding: 10px;"&amp;gt;Drop some files here&amp;lt;/div&amp;gt;
&amp;lt;output id="filesInfo"&amp;gt;&amp;lt;/output&amp;gt;
[/code]
Don’t mind the inline CSS, it is there just to make the drop target bigger. The JavaScript is veyr similar to before:
[code lang=”javascript”]
function fileSelect(evt) {
evt.stopPropagation();
evt.preventDefault();
if (window.File &amp;amp;&amp;amp; window.FileReader &amp;amp;&amp;amp; window.FileList &amp;amp;&amp;amp; window.Blob) {
var files = evt.dataTransfer.files;
var result = ”;
var file;
for (var i = 0; file = files[i]; i++) {
result += ‘&amp;lt;li&amp;gt;’ + file.name + ‘ ‘ + file.size + ‘ bytes&amp;lt;/li&amp;gt;’;
}
document.getElementById(‘filesInfo’).innerHTML = ‘&amp;lt;ul&amp;gt;’ + result + ‘&amp;lt;/ul&amp;gt;’;
} 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);
[/code]
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:
[code lang=”html”]
&amp;lt;form enctype="multipart/form-data" method="post" action="upload.php"&amp;gt;
&amp;lt;div class="row"&amp;gt;
&amp;lt;label for="fileToUpload"&amp;gt;Select Files to Upload&amp;lt;/label&amp;gt;&amp;lt;br /&amp;gt;
&amp;lt;input type="file" name="filesToUpload[]" id="filesToUpload" multiple="multiple" /&amp;gt;
&amp;lt;output id="filesInfo"&amp;gt;&amp;lt;/output&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class="row"&amp;gt;
&amp;lt;input type="submit" value="Upload" /&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/form&amp;gt;
[/code]
And the JavaScript:
[code lang=”javascript”]
function fileSelect(evt) {
if (window.File &amp;amp;&amp;amp; window.FileReader &amp;amp;&amp;amp; window.FileList &amp;amp;&amp;amp; 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 = ‘&amp;lt;img style="width: 90px;" src="’ + evt.target.result + ‘" /&amp;gt;’;
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);
[/code]
Actual HTML5 image upload
Finally, let’s upload this images to server using XMLHttpRequest:
[code lang=”javascript”]
if (window.File &amp;amp;&amp;amp; window.FileReader &amp;amp;&amp;amp; window.FileList &amp;amp;&amp;amp; 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 &amp;lt; files.length; i++) data.append(‘file’ + i, files[i]);
xhr.send(data);
};
} else {
alert(‘The File APIs are not fully supported in this browser.’);
}
[/code]
For resizing and cropping these images, we will modify the script from the last week’s tutorial (ImageManipulator class is available here):
[code lang=”php”]
// 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-&amp;gt;getWidth();
$height = $manipulator-&amp;gt;getHeight();
$centreX = round($width / 2);
$centreY = round($height / 2);
// our dimensions will be 200×130
$x1 = $centreX – 100; // 200 / 2
$y1 = $centreY – 65; // 130 / 2
$x2 = $centreX + 100; // 200 / 2
$y2 = $centreY + 65; // 130 / 2
// center cropping to 200×130
$newImage = $manipulator-&amp;gt;crop($x1, $y1, $x2, $y2);
// saving file to uploads folder
$manipulator-&amp;gt;save(‘uploads/’ . $newNamePrefix . $file[‘name’]);
echo ‘Done …’;
} else {
echo ‘You must upload an image…’;
}
}
[/code]
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:
[code lang=”javascript”]
if (window.File &amp;amp;&amp;amp; window.FileReader &amp;amp;&amp;amp; window.FileList &amp;amp;&amp;amp; window.Blob) {
document.getElementById(‘filesToUpload’).onchange = function(){
var files = document.getElementById(‘filesToUpload’).files;
for(var i = 0; i &amp;lt; 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 &amp;gt; tempH) {
if (tempW &amp;gt; MAX_WIDTH) {
tempH *= MAX_WIDTH / tempW;
tempW = MAX_WIDTH;
}
} else {
if (tempH &amp;gt; 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);
}
[/code]
The code for saving the image on the server is:
[code lang=”php”]
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.’;
}
[/code]
That’s it. I hope you learned something new and will use this knowledge on your next project.
Just the thing I was looking for. I didn’t even know you can resize images before uploading, what a cool feature.
Thanks
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
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?
How can i add a progress bar or % to the html5 upload? i have all day trying to do that but without results 🙁
When resizing client side, before uploading, the uploaded image isn’t sharp unfortunately.
Thanks for the tutorial!
Great men! Thank You!
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
I am sorry, but this never happened to me. You are just getting a black square?
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
may i know where can i get ImageManipulator.php file??
Thanks 🙂
Hi there, you can get it here.
I also updated the tutorial and put this link in the text. Thanks for pointing that out.
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
Hi Fred, the last piece of code from the tutorial is saved in file called uploadResized.php and that’s it…
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!
Comments are closed.