Javascript, JQuery, PHP, Web Development

PHP Upload Progress bar V2

This now has a Github home: Here!!

Updated: The new update can now handle stopping uploads as well and extra validation and more robust scripting.

Some of you will notice a long time ago I wrote a post on creating a upload progress bar in PHP. Well I have finally got round to recreating this script and I can thankfully say it is a lot better than my old script. It is, in fact, so different I am going to start anew here and describe from the beginning how I did it.

Ok so here we are, first thing you need to do is install uploadprogress extension for PHP. To do this simply run this command:

pecl install uploadprogress

If console tells you that pecl is not a valid command or that you need phpiz; just enter this into it:

apt-get install php5-pear
apt-get install php5-dev

Afterwards open up your php.ini and at the end put:

extension=uploadprogress.so

And restart your server like so (for Apache):

sudo /etc/init.d/apache2 restart

You must restart your server, reloading will not work.

Beleive it or not but you now have PHP upload progress identification installed. Now you just need to make a script that will upload a file and display its progress. Shall we move on?

The javascript

This is the backbone to the entire script. It is what allows the front to join with the back.

var upload_timer;
var timeout = 5000;
var u_ids = [];

$(document).ready(function(){

	// Add a new upload form
	add_upload();

	// When the "Add new upload" link is clicked add a new upload form
	$('#add_upload_form').click(function(){ add_upload(); return false; });

});

/**
 * Stops an upload
 *
 * @param id
 */
function stop_upload(id){

	var answer = confirm("Are you sure you wish to remove this upload?"), p_id = "#up_prog"+id;

	// Only remove the upload if it has not completed.
	if (answer && ($(p_id+" .uploadProgInner").css("width") != "100%") && ($("#container_outer"+id).length > 0)){

		// Form ids and write the cancelling message
		var message_id = "#up_message_prog"+id;
		$(message_id).html("<b style='color:red;'>Cancelling...</b>");

		// Make the upload go away peacefully showing the user it has been cancelled.
		setTimeout(function(){
			$("#container_outer"+id).remove();
			u_ids.splice(u_ids.indexOf(id), 1);
		}, "2000");
	}else{
		if(($(id+" .uploadProgInner").css("width") == "100%") || ($("#container_outer"+id).length == 0)){
			$("#general_error").show();
			$("#general_error span").html("<h2>File Upload Could Not Be Removed</h2><p>The file upload could not be removed. This is most likely due to the file having had already been completed or already removed by some other means.</p>");
		}
	}
}

/**
 * This function runs when a upload is submitted. It checks the
 * file field as best it can for a valid file. TBH there are some things
 * we just have to do AFTER the file has been uploaded to /temp
 *
 * @param id
 * @returns {Boolean}
 */
function check_upload(id){
	var file = $("#"+id).val(), end = file.length, start = end - 5;

	var ext = file.substring(start, end);

	// Is the file a divx extension?
	if(ext == ".divx"){
		$("#uploadForm-outer"+id).hide();
		$("#up_prog"+id).show();
	}else{
		$("#general_error").show();
		$("#general_error span").html("<h2>Wrong File Type</h2><p>You attempted to upload a file type that is not supported by this uploader. Pleae try again soon, we hope to have support for mkv, avi and other formats in their raw form.</p>");
		return false;
	}
}

/**
 * This adds a new upload form to the screen
 */
function add_upload(){

	var ts, count;

	// We make a ts as a cache buster for shit browsers cough-Opera-cough
	ts = Math.round(new Date().getTime() / 1000);

	// How many forms already exist?
	count = $(".uploadFormContainer").length;

	// So long as limit has not been reached (10)
	if(count < 10){

		// Generate the form
		$.get("/upload/add", {ts: ts}, function(data){

			// Add to the page
			$("#uploadForm_container-outer").append(data);

			// If this is the first form then add the updater iframe
			if(count == 0 && $('#uInfo_ifr').length == 0){
				$("#u_iframe_container").html("<a href="/upload/getInfo">/upload/getInfo</a>");
			}

			// Add the upload id to the list of IDs
			var e = $(".uploadFormContainer").last().find("input[name=UPLOAD_IDENTIFIER]").val();
			count_ids = u_ids.length;
			u_ids[count_ids] = e;
		});

	}else{

		// Show upload limit error
		$("#general_error").show();
		$("#general_error span").html("<h2>Upload Limit Reached</h2><p>This form has been limited to 10 uploads to protected internet connections. If you are sure you understand what your ISP allows you to upload you can open a new window and continue uploading there.</p>");
	}
}

/**
 * This gets the updated status of the uploads
 *
 * @param info
 */
function update_progress(info){

	// Scrolls through the IDs assigning the information.
	for(i = 0; i < info.length; i++){

		// Sometimes uploadprogress can return null for a upload
		if(info[i] != null){

			// Calculate how much has been uploaded
			var done = Math.floor(100 * parseInt(info[i].uploaded) / parseInt(info[i].total));

			// Ascertain the IDs for the elements needing change
			var id = "#up_prog"+info[i].id;
			var message_id = "#up_message_prog"+info[i].id;

			// Change the width of the progress bar to match done and set a message for the upload status
			$(id+" .uploadProgInner").css("width", done+"%");
			$(message_id+" span").html(done+"% Completed of "+info[i].file+" - Estimated Time Left: "+info[i].left+" at "+info[i].speed+"ps");
		}
	}

}

The Upload Form

Now we have the Javascript we need the upload form itself. This form will be gotten everytime add_upload() is called within the Javascript whilst the user is under the upload limit:

		global $session;

		$u_id = strval(new MongoId());
		?>

		<div class="uploadFormContainer" id="container_outer<?php echo $u_id ?>">
			<div class="uploadForm" id="uploadForm-outer<?php echo $u_id ?>">

				<h2>Please select a file to upload</h2>

				<form onsubmit='check_upload("<?php echo $u_id ?>")' action="/upload/to_server" target="u_ifr<?php echo $u_id ?>" method="post" enctype="multipart/form-data">
					<input type="hidden" name="UPLOAD_IDENTIFIER" value="<?php echo $u_id ?>" />
					<input type="file" name="<?php echo $u_id ?>" id="<?php echo $u_id ?>"/>

					<input type="submit" value="Upload" class="uploadFormSubmit"/>
				</form>

				
			</div>

			<div class="uploadBar" id="up_prog<?php echo $u_id ?>">
				<div class="uploadProgOuter">
					<div class="uploadProgInner">&nbsp;</div>
				</div>
				<div class="up_message_prog" id="up_message_prog<?php echo $u_id ?>">
					<span>Connecting to server...</span><a href="javascript:;" class="cancel_uploadForm" onclick='stop_upload("<?php echo $u_id ?>")'><b>Cancel</b></a>
				</div>
				<div id="up_err<?php echo $u_id ?>">

				</div>
			</div>
		</div>
		<?php

The Updater page

Now we have a form up and running (I will let you decipher the form :P) we just need the progress update page:

		// A nice little hack for telling IE we wont abuse iframe permissions
		header('P3P: CP="CAO PSA OUR"');

		global $session;

		?>
		<html>
			<head>
				<title>Upload Info Page</title>

			    <script type="text/javascript">

			    	// Get the u_ids from the parent window
					var u_ids = parent.u_ids, id_url = "";

			    	// Run the initial function
					refreshiframe();

				    function refreshiframe(){

				    	// Get the url formatted version of u_ids (upload IDs)
				    	if(u_ids != null && u_ids.length > 0){
				    		for(i = 0; i<u_ids.length; i++){
				    			// return an url of the current id
				    			if(i == 0){
				    				id_url = id_url + "ids[]="+u_ids[i];
				    			}else{
				    				id_url = id_url + "&ids[]="+u_ids[i];
				    			}
				    		}
				    	}else{
					    	id_url = "ids[]=0";
				    	}

				    	// if php detects the set $_GET then delay the refresh else do it now
				    	<?php if(empty($_GET['ids']) && !isset($_GET['ids'])){ ?>
				    		redirect();
				    	<?php }else{ ?>
				    		setTimeout("redirect()",5000);
				    	<?php } ?>

				    }

				    function redirect(){
					    // redirect with full url encoded u_ids
				    	parent.uInfo_ifr.location.href="/upload/getInfo?"+id_url;
				    }
			    </script>

			</head>

			<body>
				<script type="text/javascript">

					var info = [];

					<?php for($i = 0; $i<count($_GET['ids']); $i++){

						$upload_id = $_GET['ids'][$i];

						// This is our magic function. It gets our upload information
						$info = uploadprogress_get_info($upload_id);

						// We format the data to be printed into JS JSON arrays and echo it to screen
						if(!empty($info) && $info){ ?>
							info[<?php echo $i ?>] = {'id':'<?php echo $upload_id ?>', 'file':'<?php echo $info['filename'] ?>', 'uploaded':<?php echo $info['bytes_uploaded'] ?>, 'total':<?php echo $info['bytes_total'] ?>, 'left':'<?php echo gmdate("H:i:s", $info['est_sec']) ?>', 'speed':'<?php echo convert_size_human($info['speed_average']) ?>'};
						<?php }else{ ?>
							info[<?php echo $i ?>] = null;
						<?php } ?>

					<?php } ?>

					// Now lets update the upload progress
					parent.update_progress(info);

				</script>
			</body>
		</html>
		<?php

The Page

Now we need the other page this all goes into:

<div class="UIMessage error UIMessageConstrained UIUploadErrorMessage" id="general_error">
	<span></span>
</div>

<div class="upload_container">

<div id="uploadForm_container-outer"></div>

<div id="u_iframe_container" class="u_iframe_container"></div>

<a href="" id="add_upload_form">Add new upload</a>

</div>

Beleive it or not but that is it! You now know how to make a progress bar in PHP. Rmember this isn’t flash nor is it HTML 5 it will work on any browser with almost any settings so long as they don’t have JS turned off :P. All you need to do now is style it. Here is my style sheet to get you started:

.upload_container{
	width:600px;
	margin:auto;
}

.uploadFormContainer{
	padding:10px;
	border:1px solid #C9D7F1;
	margin-bottom:10px;
}

.uploadForm{
	border:1px solid #E1ECFE;
	background:none repeat scroll 0 0 #F0F6FF;
	padding:10px;
}

.uploadForm h2{
	margin-bottom:10px;
}

.uploadFormSubmit{
	float:right;
	font-weight:bold;
}

.uploadBar{
	display:none;
}

.uploadProgOuter{
	background:white;
	height:40px;
	padding:2px;
	border:1px solid #73A6FF;
}

.uploadProgInner{
	background:#73A6FF;
	width:10%;
	height:40px;
}

.up_message_prog{
	margin-top:7px;
}

.u_iframe_container{
	display:none;
}

.UIUploadErrorMessage{
	display:none;
	margin: 15px auto;
}

This style produces this result (or close anyway):

Any questions? If so just write a comment otherwise enjoy :).

Advertisements

8 thoughts on “PHP Upload Progress bar V2

  1. Hi there i’m very interested in your script, very nice and clear code! i’m very lost as in putting this together such as filenames and things? could you help with it or maybe create a simple zip of it for me? many thanks again

    1. I’ll try and get a github up of it here: https://github.com/Sammaye/multifile_ajax_uploader

      Reading through it again I do realise there is a few confusing bits. This is mainly cos it was designed for my own cataloguing but people seem to have got quite attached to this post.

      I am currently using a uploader very similar to this one (it’s a bit more advanced) but I reckon I can get a small sample working. I’ll see what I can do tonight.

      If you are using PHP 5.4 your code can actually differ from my own for getting upload progress since uploadprogress extension is no longer required and it actually stores the upload progress of each file within the $_SESSION of the active user.

      1. Hi there thanks for your wonderful and prompt reply,
        it’s great that you have shared such a personal development like this with the world for people like me to use and to help in my own projects, I have tried to put it together, putting into files naming them whenever I can but just can’t get it running lol maybe my lack of knowledge in this field that is the problem.
        well currently my server only has 5.3.8 installed and already has the apc and uploadprogress extensions installed hence why finding your project very interesting and got attached to your wonderful and detailed coding.
        But it would be fantastic if you could do that for me, it’s only a personal project so family can use their smart phones to upload photos and videos to my server and to get this running and working would benefit them greatly for sharing with the rest of our family of the long distance and abroad,

        Many thanks for your help =]

    2. I tinkered for about 30 mins and came up with what’s in that Git repo on Github now. It works perfectly for me in all browsers I had installed (which is like 15 and counting).

      Let me know if you get problems.

      Yea, this was originally a personal project of mine and still kinda is. Everything you see here is personal since I am not actually allowd to blog my work :P. But I needed to add more code to Github since it’s way more professional to show a working repo and this is one of the more visited pages on my blog I kept overlooking so it has benefited me helping you out as well :P.

      1. Fantastic! I downloaded a fresh copy of the files there and extracted to a subfolder on my website but when I access that sub folder it’s just a blank page? i’m using the latest google chrome at the moment and my server is a windows based apache installation? don’t know what’s going on with it? the code looks perfect and don’t see nothing wrong with it :S I could privately send you the link to my personal server? and you can take a look just in case it’s my laptop that’s at fault?

        it’s a wonderful personal project, I really love the coding on it and prefer it to other projects out there that i’ve been searching through over the past day or two, it’s simple, unique and yet so clear and clean ^_^ ah well thank you for posting your personal stuff other than work haha

        Well I do agree but looking at your site and the creations you have upon it, it’s professional as it is as you clearly know what you are doing and accomplish what you set out to do 🙂

    3. It is really hard to debug a server envo without actually being there however the first steps I would take are:

      – Check the folder is actually in my webroot and is accessible from apache
      – Press f12 in Chrome, select the console tab and see if there are any errors. The script is designed to run from webroot so if you place it in a sub folder within your website you will need to modify the AJAX calls to work from there.

      1. Thank you ever so much! it was the ajax calls, removed all the ‘/’ in front of the php files listed within the files and it’s all working now, thank you very very much! it’s wonderful creation! much better than there others out there! thank you very much! 😀 now just to tie in an file pressing script and that’s it, I’m really grateful for what you have created and the support you have shown

        Thanks so much! 😀

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s