Thursday, May 24, 2012

JQUERY AJAX style upload with callback

By now, you have probably seen websites with "AJAX" style upload. Technically many of them are not really AJAX. XMLHttpRequest (AJAX) upload is not technically possible due to security limitations of Javascript.  However, we still call them AJAX uploaders because they act and feel like it to the end-user; meaning the web pages are posting files without refreshing.

So where are the "AJAX uploaders?"


Many of them are actually  SWF (Flash based) uploaders billed as "jquery file upload plugins." I'm sure many of them work great but I prefer to avoid Flash as much as possible.

There are also HTML5 support in some new browsers for asynchronous file uploads via AJAX post but I've had problems with some browsers like Safari and problems with different file types.


Today, I will show you how to simulate an AJAX upload without the use of Flash. If you have done some googling, the most common way to do it is to use a hidden iframe. This is considered a hack but it works. There are some tutorials out there but mine will show you how to get a callback from your upload script using Jquery. You can use pure Javascript but Jquery is very convenient.
A callback will be a JSON reply that the host page (the one doing the upload) can retrieve and act upon. For example, if the upload failed, you can notify the user. Or you can pass the record ID of the file after it was stored in a database.


 First, you need to add a hidden iframe (mine is called upload_hidden) to your upload page.





 <body>  
 <iframe id="upload_hidden" name="upload_hidden" src="blank.html" style="display:none;"></iframe>  
 <form enctype="multipart/form-data" method ="POST" action ="upload_json.php" id ="upload_form">  
 <input type="file" name="upload_file"><button name="Upload" type="submit" value ="Upload">Upload</button>  
 </form>  
 </body>  


Then you need to set the form to post to the hidden frame.


 function setTarget() {  
   document.getElementById('upload_form').onsubmit=function() { document.getElementById('upload_form').target = 'upload_hidden';}  
 }  

 Then make sure you call it onload.


 window.onload=setTarget;  


Now for the pseudo callback. The trick is to check every time the iframe loads new content and parse the results. The way I do it is to embed my JSON reply in a div from the upload script.

After processing my upload, my PHP code generates the JSON wrapped in a DIV.


 <?php  
 $finished = array ('status'=>'success','time'=>date('Y-m-d H:i:s',time()),'db_insert_id'=>$record_id, );   
 echo "<div id ='upload_status'>";  
 echo json_encode($finished);  
 echo "</div>"; ?>  

And here is my JQuery code to parse the JSON from the PHP loaded into the hidden div.
  $(document).ready(function() {  
   $('#upload_hidden').load(function() {  
     var a = $("#upload_hidden").contents().find("#upload_status").html();  
     if (a !=null) {  
     var obj = jQuery.parseJSON(a);    
       if (obj.status == 'success') {  
           alert ("file to saved to db as " + obj.db_insert_id);      
         } // #end success  
       } // #end a!=null  
     }); // #end upload_hidden load  
 });  

The key thing to note are:
a = $("#upload_hidden").contents().find("#upload_status").html();
 and
obj = jQuery.parseJSON(a)

Every time the iframe is loaded, I look inside the iframe for anything inside a div called "upload_status." When the iframe is initially loaded with a blank placeholder, nothing happens because the content is empty. However, when it detects anything, I parse whatever is inside the div as my JSON string.


After uploading a file, here are the results. Obviously, you would need to hide the iframe after you do some testing.

To wrap up. This is one way to handle callbacks in an pseudo-AJAX file upload. There are probably other ways to do it but this was something quick and works for my needs.
Here is the example code.
 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">  
 <html xmlns="http://www.w3.org/1999/xhtml">  
 <head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />  
 <title>Example</title>  
 <script type="text/javascript" src="http://code.jquery.com/jquery-latest.js"></script>  
 <script type="text/javascript">  
 function setTarget() {  
   document.getElementById('upload_form').onsubmit=function() { document.getElementById('upload_form').target = 'upload_hidden';}  
 }  
 $(document).ready(function() {  
   $('#upload_hidden').load(function() {  
     var a = $("#upload_hidden").contents().find("#upload_status").html();  
     if (a !=null) {  
     var obj = jQuery.parseJSON(a);    
       if (obj.status == 'success') {  
           alert ("file to saved to db as " + obj.db_insert_id);      
         } // #end success  
       } // #end a!=null  
     }); // #end upload_hidden load  
 });  
 window.onload=setTarget;  
 </script>  
 </head>  
 <body>  
 <iframe id="upload_hidden" name="upload_hidden" src="blank.html" style="display:none;"></iframe>  
 <form enctype="multipart/form-data" method ="POST" action ="upload_json.php" id ="upload_form">  
 <input type="file" name="upload_file">  
   <button name="Upload" type="submit" value ="Upload">Upload</button>  
 </form>  
 </body>  
 </html>  

No comments:

Post a Comment