Look Inside

Over the last week I’ve been trying to learn about HTML5 Drag and Drop (DnD) File Upload using Drag and Drop API combined with HTML5 File API. Drag and Drop API enables browsers to receive local files from user desktop just by ‘drag‘ and then ‘drop‘. While HTML5 File API enables JavaScript to interact with selected local files before upload to the server. With both of them, we can build a drag and drop file upload interface.

I’ve created an example of HTML5 drag and drop file upload with the capabilities to resize, crop or reformat/filter the uploaded image through HTML5 canvas before sending to the server. The techniques included are:

You could check out the working demo through the link up there. Have fun with this HTML5 drag and drop file upload with canvas!

[browser type=”cfs-x”]This working demo only works in browsers that support HTML5 File API as well as Drag and Drop API.[/browser]

The HTML Markup

The demo consists of two major parts, an area for drag and drop as well as the upload preview part. The idea is to capture the files uploaded by user whether through drag and drop area or browse button, and then display it in the preview section before upload to the server.

<!-- drop area --> <div id="droparea"> <div class="dropareainner"> <p class="dropfiletext">Drop files here</p> <p>or</p> <p><input id="uploadbtn" type="button" value="Select Files"/></p> <!-- extra feature --> <p id="err"><!-- error message --></p> </div> <input id="upload" type="file" multiple /> </div> <!-- result area --> <div id="result"></div>

Also, we will using jQuery Template to create image preview wrapper which contains preview of uploaded image, image name, original image size, and final size of image that will upload to the server.

<script id="imageTemplate" type="text/x-jquery-tmpl"> <div class="imageholder"> <figure> <img src="${filePath}" alt="${fileName}"/> <figcaption> ${fileName} <br/> <span>Original Size: ${fileOriSize} KB</span> <br/> <span>Upload Size: ${fileUploadSize} KB</span> </figcaption> </figure> </div> </script>

Events Binding

Now, let’s get our hand dirty with some jQuery coding. The first thing that we are going to do is to bind the Drag and Drop events to our #droparea element. There are a number of drag events, but we will use only dragover , dragleave , and drop events.

var dropzone = $('#droparea'); dropzone.on('dragover', function() { //add hover class when drag over dropzone.addClass('hover'); return false; }); dropzone.on('dragleave', function() { //remove hover class when drag out dropzone.removeClass('hover'); return false; }); dropzone.on('drop', function(e) { //prevent browser from open the file when drop off e.stopPropagation(); e.preventDefault(); dropzone.removeClass('hover'); //retrieve uploaded files data var files = e.originalEvent.dataTransfer.files; processFiles(files); return false; });

dataTransfer.files will returns a FileList of the files being dragged.

Not only drag and drop area, instead we will create a ‘browse’ button as well. But we don’t want to use native browser file upload button, so here is a little trick for this.

var uploadBtn = $('#uploadbtn'); var defaultUploadBtn = $('#upload'); uploadBtn.on('click', function(e) { e.stopPropagation(); e.preventDefault(); //trigger default file upload button defaultUploadBtn.click(); }); defaultUploadBtn.on('change', function() { //retrieve selected uploaded files data var files = $(this)[0].files; processFiles(files); return false; });

The best practice of using new HTML5 objects is to check for object availability before using it, else simply skips the function or show a fallback plan if any.

function processFiles(files) { //check for browser support if(files && typeof FileReader !== "undefined") { //extract FileList as File object for(var i=0; i<files.length; i++) { readFile(files[i]); } } else { //some message or fallback } }

Each File object contains properties like name , size , and type .

The FileReader Object

Next, we will read the content of uploaded file through FileReader object which allows browser asynchronously to read files from user desktop.

var readFile = function(file) { if( (/image/i).test(file.type) ) { //define FileReader object var reader = new FileReader(); //init reader onload event handlers reader.onload = function(e) { var image = $('<img/>') .load(function() { //get new image URL from canvas image var newimageurl = getCanvasImage(this); //create preview using jQuery Template createPreview(file, newimageurl); //upload the new image to server uploadToServer(newimage, imageObj); }) .attr('src', e.target.result); }; //begin reader read operation reader.readAsDataURL(file); } else { //some message for wrong file format } }

Once the reader is loaded, we can bind the uploaded image (in Base64-encoded) to <img> tag through e.target.result . After that, we will use it to draw a canvas image so that we can modify it with JavaScript before proceed to the server. This definitely will save the bandwidth compare to process it at server side.

Canvas Image

We can resize the image as well as apply different filters to our canvas image. However I won’t cover them here, still, you can grab them from the source files. When the canvas image was drawn, convert it to file URL format through toDataURL method and attach it to jQuery Template.

var getCanvasImage = function(image) { var imgWidth = 180, imgHeight = 180; //define canvas image var canvas = document.createElement('canvas'); canvas.width = imgWidth; canvas.height = imgHeight; var ctx = canvas.getContext('2d'); ctx.drawImage(image, imgWidth, imgHeight); //convert canvas to jpeg URL return canvas.toDataURL("image/jpeg"); }

jQuery Template

When we have everything we need, simply bind them to jQuery Template and append to our #result element.

var createPreview = function(file, newURL) { //populate jQuery Template binding object var imageObj = {}; imageObj.filePath = newURL; imageObj.fileName = file.name.substr(0, file.name.lastIndexOf('.')); //subtract file extension imageObj.fileOriSize = convertToKBytes(file.size); imageObj.fileUploadSize = convertToKBytes(dataURItoBlob(newURL).size); //convert new image URL to blob to get file.size //append new image through jQuery Template var img = $("#imageTemplate").tmpl(imageObj).prependTo("#result"); }

There is a need of converting newURL (in DataURI format) to Blob so that we can access it’s attributes like file size or file type whether from client side or server side.

For dataURItoBlob function, you might found them in script.js file inside source files.

Upload To Server

Last but not least, we can upload the new image to our server through jQuery AJAX .

var uploadToServer = function(oldFile, newFile) { // prepare FormData var formData = new FormData(); //we still have to use back some of old file data //since new file doesn't contains original file data formData.append("filename", oldFile.name); formData.append("filetype", oldFile.type); formData.append("file", newFile); //submit formData using $.ajax $.ajax({ url: "upload.php", type: "POST", data: formData, processData: false, contentType: false, success: function(data) { //some success code here } }); }

The File Upload PHP Script

Though this is not a complete PHP upload script, but hope this will helps you get to know how it works inside the back end code.

if(isset($_FILES['file'])) { if(move_uploaded_file($_FILES["file"]["tmp_name"], "upload/".$_POST["filename"])) { echo 'Upload Success'; } else { echo '#Fail'; } }

View Demo Download Source