HTML5 Drag and Drop File Upload

HTML5 Drag and Drop File Upload with Canvas

Written by Kenny on May 23, 2012 HTML5
HTML5 drag and drop (dnd) file upload with canvas using Drag and Drop API, HTML5 File API, jQuery Template and CSS3 example with tutorial.

HTML5 Drag and Drop File Upload with Canvas

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.

HTML5 Drag and Drop File Upload

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!

This working demo only works in browsers that support HTML5 File API as well as Drag and Drop API.

 

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


» This Might Interest You «

User Feedbacks (31) Share Your!

  • it could be great with few things like:
    //define canvas
    var canvas = document.createElement(‘canvas’);
    //define ration
    alert(image.width);
    alert(image.height);

    if(image.width!=image.height)
    {
    var ratio=image.width/imgWidth;
    imgWidth=image.width/ratio;
    imgHeight=image.height/ratio;
    alert(imgWidth);
    alert(imgHeight);
    }

    canvas.width = imgWidth;
    canvas.height = imgHeight;
    var ctx = canvas.getContext(’2d’);

    and also… the possibility to delete uploaded photos…. another thing can be… how to upload with other things like c++/c#/java etc…. im not javascript programmer… but this with the ratio was the only thing i could do…

    4 months ago! Reply
  • jay

    Nice work !!
    It works with browsers Firefox, Chrome and IE10 but not with IE11, I get error “InvalidStateError” on line 142.
    Any chance you will be working on this issue?
    I tried to change that part of the script but without success.

    Best regards.
    j

    1 year ago! Reply
  • Tarun

    Hello Every one,

    I want to give the restriction to user upload only 15 files so what can i do in that code and also remove button to click and remove photo

    Please help me and what can i do in that.

    1 year ago! Reply
  • Tarun

    Hi,

    I want to give the restriction to user upload only 15 files so what can i do in that code

    Please help me and what can i do in that.

    1 year ago! Reply
  • Tanas Alexandru Florin

    Helo Kenny

    Thanks for this nice script.
    Tell me please its posibble insert link on images after upload and send to the resize image ? if its posibble tel my how :)

    Many thanks

    1 year ago! Reply
    • Tanas Alexandru Florin

      Hello Kenny

      Thanks for this nice script.
      Tell me please its posibble insert link on images after upload and send to the resize image (upload folder) ? if its posibble tell me how :)

      Many thanks

      1 year ago! Reply
  • shiva

    progress bar not working for me? its showing NAN%?

    1 year ago! Reply
  • shivali

    Its amazing uploader.
    can u please tell me on which browser it is supported and with which version.

    2 years ago! Reply
  • Hello Kenny,

    thanks for the great tutorial and demo example!

    I have two questions:

    - it’s possible to easy implement new effects? For example http://www.script-tutorials.com/html5-image-effects-sepia/ i just need to add a new radio button in the html and assign an ID, then in your script.js at line 143 just copy and paste first part of script.js listed in that web site?

    - it’s possible to implement a “download image” button or link for each image so a user can download the “processed” image with the effect applied?

    Thanks in advice!

    2 years ago! Reply
    • Hi Simone,

      Sorry for late replying. You’re welcome!

      For first question, you can check out new demonstration page, sepia image effect had been added to the script.js from the link you provided. And it should not be difficult to add other new effects.

      Regarding your second question, the output of “processed” image is already in image format, you can simply bind the src of image to a download link perhaps. =)

      2 years ago! Reply
  • HTML5 really has many cool stuff, thanks for sharing I learn a lot.

    2 years ago! Reply
  • MM

    Hej, this script is very greatfull ;)
    But i have only one question, where in this script is information about width and height image ?
    I just want crop the image what was more them 1024 for example, and i have not got idea how can do that.

    2 years ago! Reply
  • MIchael

    Wow this is amazing! thanks for sharing!!

    Is there any way to improve the image quality of the resampled images? All of my images are showing some white pixels. may be thee need some smooting?

    thanks

    Michael

    2 years ago! Reply
  • Arihant

    hey i cannot make the locally downloaded source of your app on chrome though it runs fine in firefox

    The online version though runs fine in chrome.Can you please tell anything on how to run it locally.

    Thanks for nice tutorial.Learned a lot.

    2 years ago! Reply
  • Loganathan

    Very excellent description, thanks a lot. I want to add “queuefile” functionality how it can be achieved?

    2 years ago! Reply
  • Admiring the hard work you put into your blog and detailed
    information you present. It’s good to come across a blog every once in a while that isn’t the same unwanted rehashed information. Excellent read! I’ve bookmarked your site and I’m including your RSS feeds to my Google account.

    2 years ago! Reply
  • techzombie

    I want to open this up for mp3′s and much bigger file uploads. will you be adding an upload progress bar in the future? would make a great part 2 :)

    2 years ago! Reply
    • SpaceDog

      FWIW, I took the ideas in this post and extended them to provide upload progress using a native progress bar. If you’re using the techniques in this post, then you’ve probably already decided that users will have browsers that are modern enough to render the HTML5 “progress” element. So the only things you need to do here are a.) create a progress element in your HTML; b.) attach a handler to your XMLHttpRequest’s upload.onprogress event and, inside of your handler, set the progress element’s ‘value’ attribute to the calculated percent of how much has been uploaded. No server-side hacks necessary. The progress event contains ‘loaded’ and ‘total’ properties; to get the percent you just do “var percent = parseInt( (event.loaded * 100) / event.total );”. Voila. To get it working with jQuery, just create your own XHR object manually, set your progress handler on it, and then when you use the $.ajax call remember to set its “xhr” property to the XHR object you created manually (you’re basically telling jQuery, “Use _my_ XHR transport object instead of your own”).

      Note: To get native upload progress status, you’ll need a browser that has XHR2 support (Chrome does, don’t know about FF or others) and that also supports the progress element. Assuming that, you could take this tutorial as-is and add only about 10 lines of code to achieve what I’m talking about. I’m only adding this comment here because to piece together everything I needed took me an entire night and I’d rather spare people the task.

      2 years ago! Reply
      • techzombie

        Could u share these 10 lines with us?

        Alot of us don’t know jquery well enough to imlement a progress bar without weeks of pain. Please help :)

        2 years ago! Reply
  • SpaceDog

    This worked beautifully. I’m developing a intranet webapp that’s 100% AJAX-driven (no page reloads whatsoever), so uploading images without a page reload was critical. I do need image preview functionality; however, readers should note that the Canvas API is not necessary if you don’t need to modify the image at all. I simply used the image in the reader.onload function as the preview image. One other thing to note is that the FileReader will fail to load the image data if the page is loaded locally from the filesystem; it needs to be served from a real server.

    2 years ago! Reply
    • Hi SpaceDog,

      Yes you’re right! The Canvas API is not necessary for preview but I do use Canvas API is to show the possibility to resize the uploaded image before sending to the server, since this can save us a lot of bandwidth.

      Thanks for your note!

      2 years ago! Reply
    • techzombie

      I don’t want to use canvas either…

      My upload script writes out a thumb . how can i use that?
      I am having trouble replacing this line

      var newimageurl = getCanvasImage(this);

      What do I replace that line with so the upload works?

      2 years ago! Reply
  • jason

    thank you very much for your code. It’s simple and great.

    3 years ago! Reply

Leave a Feedback

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>