Ajax has transformed the web entirely by providing high level of interaction with web pages and superior design capabilities. It is widely used to send and receive data without the need to refresh the pages. If you are programming with Javascript or jQuery, at one point of time you might have used Ajax to submit simple html forms. But till today uploading files using Ajax is still a mystery to many. But not a mystery if you know FormData object and FileReader API.
The purpose of this article is to provide a clear example of how to write a file upload using jQuery ajax with drag and drop support. Though the actual code is written in jQuery, we will not be able to achieve a pure jQuery solution, but we need to fallback to the native methods of Javascript to do it. And before going in to the article, I like to bring to your notice that you must consider the browser support while implementing ajax file upload.
Compatibility: Firefox, Chrome and Opera.
We will be using HTML, jQuery and PHP in creating this ajax file upload. First lets take a look at the HTML part and lets consider the features we will be implementing.
<input type="file" id="fileToUpload" multiple="multiple"><br/> <div id="total"> </div> <button id="upload_btn">Start Uploading</button><br/> <div class="progressbar" id="pb"> <div></div> </div> <div id="log"></div> <div id="status"></div> <div id="uplcomp"></div>
- Line 1 – a file input field with multiple attribute, so you can select multiple files.
- Line 2 – below the file input we have div that will act as a drop zone for file drag and drop operations, also it will display the list of selected files and their size.
- Line 3 – a button that initiates the upload which will become active once the files are selected or dropped on the drop zone.
- Line 4 – a div for progress bar which will be updated as file is uploaded.
- Line 7 – a div that will display the file that is being currently uploaded.
- Line 8 – a div for showing the number of kb uploaded and total kb of the file.
- Line 9 – a div to show the files that has been uploaded successfully.
First we will attach the change event to our input field.
$( "#fileToUpload" ).change( function() { var fileIn = $( "#fileToUpload" )[0]; if( !upfiles ) upfiles = fileIn.files; else { if( fileIn.files.length > 0 ) { if( confirm( "Drop: Do you want to clear files selected already?" ) == true ) upfiles = fileIn.files; else return; } } $( '#total' ).html( '<b> Selected Files </b><br />' ); $('#upload_btn').prop("disabled", false); $(upfiles).each( function(index, file) { size = Math.round( file.size / 1024 ); if( size > 1024 ) size = Math.round( size / 1024 ) + ' mb'; else size = size + ' kb'; $( '#total' ).append( file.name + " ( " + size + " ) <br />" ); }); });
The above code will get the list of files and calculates the size of each file and displays them in the div below the input field.
$(upfiles).each( function(index, file) { size = Math.round( file.size / 1024 ); if( size > 1024 ) size = Math.round( size / 1024 ) + ' mb'; else size = size + ' kb'; $( '#total' ).append( file.name + " ( " + size + " ) <br />" ); });
We have used .each() method on the “upfiles” object which currently holds list of file objects, .each() acts as a loop iterating through all the file objects and calculating the size of each file. Now let us include the code for drag and drop support.
$( '#total' ).bind( 'dragover',function(event) { event.stopPropagation(); event.preventDefault(); }); $( '#total' ).bind( 'drop',function(event) { event.stopPropagation(); event.preventDefault(); if( upfiles == 0 ) upfiles = event.originalEvent.dataTransfer.files; else { if( confirm( "Drop: Do you want to clear files selected already?" ) == true ) { upfiles = event.originalEvent.dataTransfer.files; $( '#fileToUpload' ).val(''); } else return; } $( "#fileToUpload" ).trigger( 'change' ); });
We have ‘dragover’ and ‘drop’ events attached to the div, and we have stopped the event propagation and default behavior of the browser when some files are dragged on to the viewport. Also you must take a notice that while you drop the files into the view port, the originalEvent object will have the files.
Now the ajax part where we upload the files.
$("#upload_btn").click( function() { if ( upfiles ) { // trigger the first 'upload' - custom event. $( '#fileToUpload' ).trigger('upload'); $(this).prop("disabled", true); } else { alert( "Please select a file" ); return; } }); // the ajax part for file upload $( '#container' ).on( 'upload', '#fileToUpload' , function( ) { var $this = $(this); //check if we have files if ( typeof upfiles[count] === 'undefined') return false; var data = new FormData(); var file = upfiles[count]; data.append( 'file', file ); currfile = file.name; fileext = currfile.substr( -3 ); // check for files not allowed for upload if( fileext == "php" || fileext == ".js" ) { count++; $( '#uplcomp' ).append( 'File not allowed - ' + file.name + '<br />' ); $this.trigger('upload'); } else { $.ajax({ url: 'files.php', // php script that will process the uploaded files type:'POST', // type must be set to POST data: data , // data - FormData object that contains the file contentType: false, // must be set to false - we should not set content type processData: false, // must be set to false beforeSend: function( ) { $('#log').text( 'Uploading ' + file.name ); $(".progressbar").show(); }, xhr: function() { var xhr = $.ajaxSettings.xhr(); if(xhr.upload){ xhr.upload.addEventListener( 'progress', showProgress, false); } return xhr; }, success: function( data ) { count++; if( percentComplete <= 100 ) { $('#pb div').animate({ width: '100%' }, { step: function(now) { $(this).text( Math.round(now) + '%' ); },duration: 10}); } $( '#uplcomp' ).append( data ); $( '#log' ).text( ' ' ); $this.trigger('upload'); }, error: function( ){ alert( "Upload Canceled or Failed"); } }); } });
See in the above code we have attached the click event for the upload button, which triggers a custom event ‘upload’ that initiates the ajax request. We have also ensured that the multiple files are uploaded one by one instead of setting up multiple ajax request simultaneously.
We have used the low level ajax call to send the Xml HTTP Request ( xhr ). jQuery ajax has the xhr setting through which we get the actual data transfer status, which will be useful in showing the progress of the file upload. Also note that we have used FormData object to send the files, the other method you can use to send the file is FileReader API.
var data = new FormData(); var file = upfiles[count]; data.append( 'file', file );
The above three lines makes the file upload possible. We create an instance of FormData and append the files to the object and send the object in the data field of the ajax call. As of now most of the major browsers support FormData object, except IE8 and IE9. Hope it will be working in IE 10.
Finally our progress bar function.
function showProgress(evt) { if (evt.lengthComputable) { percentComplete = (evt.loaded / evt.total) * 100; $('#pb div').animate( { width: percentComplete + '%' }, { step: function(now) { $(this).text(Math.round(now) + '%'); }, duration: 10 }); $( '#status' ).text( 'Uploaded ' + Math.round( evt.loaded / 1024 ) + ' kb of ' + Math.round( evt.total / 1024 ) + ' kb' ); } }
We have attached an event listener to the progress event in the xhr( Xml http request ) settings of ajax call, with our callback function “showProgress”, which will receive the actual event object that will contain the following properties
- loaded – number of bytes loaded
- total – total number of bytes ( actual file size );
- lengthComputable – which will be set to true if there is support for calculation of file upload progress.
We use the above values and use .animate() to animate the size of the div. You can download the completed code along with the PHP file that process the upload at the server side.
Hello friend,
Well done , it is very beautiful and useful example as always ! ! !
We learnt a lot from you’re nice example ! ! !
You are a code saver 🙂
Thank you in advance.
Best Regards,
Tasos
Thank You