Plupload Custom File Upload UI

Plupload is a tool that allows you to upload files using HTML5 Gears, Silverlight, Flash, BrowserPlus, normal forms, and provides features such as upload progress, image resizing and chunked uploads.

So I’m gonna discuss the process I went through to implement a custom Plupload UI.

Here is a video off it in action:

Now I’ll go into details on how it was built using the Plupload framework.
I modeled my version of the plupload custom example here.

So first of all I Init an instance of plupload and set the options. I set the drop area, and the container that the file progress will be displayed in.
Note: Only FF and Chrome support drag’n drop in Plupload.

$(document).ready(function(){
var uploader = new plupload.Uploader({
	// General settings
	runtimes: 'html5,flash,gears,browserplus,silverlight,html4',
	url:<%= SaveUrl %>,
	browse_button : "pickfiles",
	button_browse_hover : true,
	drop_element : "dropArea",
	autostart : true,
	max_file_size: '100mb',
	container: "FileContainer",
	chunk_size: '1mb',
	unique_names: true,
	// Flash settings
	flash_swf_url: <%= "'"+  Page.ResolveUrl("~/plugins/plupload/plupload.flash.swf") +"'" %>,
	// Silverlight settings
	silverlight_xap_url: <%= '"'+ Page.ResolveUrl("~/plugins/plupload/plupload.silverlight.xap") +'"' %>
});

Plupload doesn’t automatically hide the drop area when the current browser doesn’t support it so I bind some custom logic to the plupload “Init” event below. Drag and drop support requires that the browser support FileReader and will not work under the flash or silverlight runtimes.

uploader.bind('Init', function(up, params){
try{
    if(!!FileReader && !((params.runtime == "flash") || (params.runtime == "silverlight")))
         $("#dropArea").show();
         $("#fileSelectMsg").hide();
    }
catch(err){}});
uploader.init();

So now we need to be able to display files in our “FileContainer” div the script below fires on the FilesAdded event. It is called when a file is added or dropped in the drop area. When this event is fired I create a div that contains the elements we will need to display upload progress and the name of the file.

uploader.bind('FilesAdded', function(up, files) {
	$.each(files, function(i, file) {
		$('#filelist').append(
			'<div id="' + file.id + '" class="fileItem"><div class="name">' +
			file.name + '</div><a href="#" id="cancel'+file.id+'" class="cancel">cancel</a><div class="fileInfo"><span class="size">' + plupload.formatSize(file.size) + '</span>' +
			'<div class="plupload_progress"><div class="plupload_progress_container"><div class="plupload_progress_bar"></div></div></div>'+
			'<span class="percentComplete"></span></div></div>');

		//Fire Upload Event
		up.refresh(); // Reposition Flash/Silverlight
		uploader.start();

		//Bind cancel click event
		$('#cancel'+file.id).click(function(){
			$fileItem = $('#' + file.id);
			$fileItem.addClass("cancelled");
			uploader.removeFile(file);
			$(this).unbind().remove();
		});

		//Set ico_Ok to Uploading
		$confirmNext.attr("disabled", "disabled");
		$confirmNext.html("Uploading..");
		$confirmNext.unbind("click").click(function(e){e.preventDefault();});
	});
});

Now that we have files uploading we need to update the progress bars for each file div in the “FileContainer”. So yet again Plupload conviently has an event called “UploadProgress” that fires after each chunk has been successfully uploaded.

uploader.bind('UploadProgress', function(up, file) {
	var  $fileWrapper = $('#' + file.id);
	$fileWrapper.find(".plupload_progress").show();
	$fileWrapper.find(".plupload_progress_bar").attr("style", "width:"+ file.percent + "%");
	$fileWrapper.find(".percentComplete").html(file.percent+"%");
	$fileWrapper.find('#cancel'+file.id).remove();
});

So now we have files uploading and being added to the container and updating the upload progress in real time. So now I want to grey out the div once the file has been completed. In order to do this I add a class to that file’s div. The “FileUploaded” event gets fired after each file is successfully uploaded.

uploader.bind('FileUploaded', function(up, file) {
	$fileItem = $('#' + file.id);
	$fileItem.addClass("completed");

	$confirmNext.removeAttr("disabled");
	$confirmNext.html("Ok");
	$confirmNext.unbind().click(function(e){window.location.href = window.location.href;});

	$('#cancel'+file.id).unbind().remove();
});

Last but not least I’ll show you how to display errors that happen server side when a chunk is uploaded. On “ChunkUploaded” event I check the response message if it contains “Error:” in the response string. I kill the current upload and display that error message to the user and also log the error in my application. The following snippet performs just this.

uploader.bind("ChunkUploaded", function(up, file, response){
	//Should return a status 200 if the chunk was uploaded successfully
	if(response.status != null)
	{
		if(response.status != "200" || (response.response.indexOf("Error") != -1))
		{
			if(response.response.indexOf("Error") != -1)
			{
				//Prompt the user with the custom error message
				$("div.error:first").show().html('<p>'+response.response+'</p>');
			}
			else
			{
				//Log this as an error
				//Custom line of code to log error on server would go here
				
				//Notify user of error
				$("div.error:first").show().html('<p>There was an error uploading your file '+ file.name +' Support has been notified.</p>');
			}
			$('#' + file.id).addClass("cancelled");
			uploader.stop();
		}
	}    
});

So thats how I built my custom UI wrapper around the Plupload core. Hopefully that will help get you going in the right direction.