Hi Wes, Struts’ ajax file upload would be very interesting to me since I have implemented jQuery (http://www.phpletter.com/Our-Projects/AjaxFileUpload/) and Struts for ajax file upload in my application and had one or two problem(s).
FYI: My use case is to upload a file, together with utf8 encoded caption. The problem I had is utf8 encoded caption needs to be sent via HTTP POST, it won’t work with HTTP get. But the jQuery arrangement is sending file in POST while sending caption in Get. So what I did is to send caption and file in two POSTs which cause other complications. Later the author (Øyvind Saltvik oyvind.salt...@gmail.com) sent me the following code contributed by (Prokofiev Nikolay nprokof...@gmail.com) which added the parameter POSTing (e.g. caption) to jQuery’s file upload. $.ajaxFileUpload ( { url:document.location+'&ajax=true', secureuri:false, fileElementId:'fileToUpload', data: {foo: 'ghffgh', bar: 'fghfhgg'}, dataType: 'json', success: function (data, status) { if(typeof(data.error) != 'undefined') { if(data.error != '') { alert(data.error); }else { alert(data.msg); } } }, error: function (data, status, e) { alert(e); } } ) here is the script: jQuery.extend({ createUploadIframe: function(id, uri) { //create frame var frameId = 'jUploadFrame' + id; if(window.ActiveXObject) { var io = document.createElement('<iframe id="' + frameId + '" name="' + frameId + '" />'); if(typeof uri== 'boolean'){ io.src = 'javascript:false'; } else if(typeof uri== 'string'){ io.src = uri; } } else { var io = document.createElement('iframe'); io.id = frameId; io.name = frameId; } io.style.position = 'absolute'; io.style.top = '-1000px'; io.style.left = '-1000px'; document.body.appendChild(io); return io }, createUploadForm: function(id, fileElementId, postData) { //create form var formId = 'jUploadForm' + id; var fileId = 'jUploadFile' + id; var form = $('<form action="" method="POST" name="' + formId + '" id="' + formId + '" enctype="multipart/form-data"></form>'); var oldElement = $('#' + fileElementId); var newElement = $(oldElement).clone(); $(oldElement).attr('id', fileId); $(oldElement).before(newElement); $(oldElement).appendTo(form); try { for(param in postData) { var input = document.createElement('input'); input.setAttribute('name', param); input.setAttribute('value', postData[param]); $(input).appendTo(form); } } catch(e) {} //set attributes $(form).css('position', 'absolute'); $(form).css('top', '-1200px'); $(form).css('left', '-1200px'); $(form).appendTo('body'); return form; }, ajaxFileUpload: function(s) { // TODO introduce global settings, allowing the client to modify them for all requests, not only timeout s = jQuery.extend({}, jQuery.ajaxSettings, s); var id = new Date().getTime() var form = jQuery.createUploadForm(id, s.fileElementId, s.data); var io = jQuery.createUploadIframe(id, s.secureuri); var frameId = 'jUploadFrame' + id; var formId = 'jUploadForm' + id; // Watch for a new set of requests if ( s.global && ! jQuery.active++ ) { jQuery.event.trigger( "ajaxStart" ); } var requestDone = false; // Create the request object var xml = {} if ( s.global ) jQuery.event.trigger("ajaxSend", [xml, s]); // Wait for a response to come back var uploadCallback = function(isTimeout) { var io = document.getElementById(frameId); try { if(io.contentWindow) { xml.responseText = io.contentWindow.document.body?io.contentWindow.document.body.innerHTML:null ; xml.responseXML = io.contentWindow.document.XMLDocument?io.contentWindow.document.XMLDocument: io.contentWindow.document; }else if(io.contentDocument) { xml.responseText = io.contentDocument.document.body?io.contentDocument.document.body.innerHTML: null; xml.responseXML = io.contentDocument.document.XMLDocument?io.contentDocument.document.XMLDocum ent:io.contentDocument.document; } }catch(e) { jQuery.handleError(s, xml, null, e); } if ( xml || isTimeout == "timeout") { requestDone = true; var status; try { status = isTimeout != "timeout" ? "success" : "error"; // Make sure that the request was successful or notmodified if ( status != "error" ) { // process the data (runs the xml through httpData regardless of callback) var data = jQuery.uploadHttpData( xml, s.dataType ); // If a local callback was specified, fire it and pass it the data if ( s.success ) s.success( data, status ); // Fire the global callback if( s.global ) jQuery.event.trigger( "ajaxSuccess", [xml, s] ); } else jQuery.handleError(s, xml, status); } catch(e) { status = "error"; jQuery.handleError(s, xml, status, e); } // The request was completed if( s.global ) jQuery.event.trigger( "ajaxComplete", [xml, s] ); // Handle the global AJAX counter if ( s.global && ! --jQuery.active ) jQuery.event.trigger( "ajaxStop" ); // Process result if ( s.complete ) s.complete(xml, status); jQuery(io).unbind() setTimeout(function() { try { $(io).remove(); $(form).remove(); } catch(e) { jQuery.handleError(s, xml, null, e); } }, 100) xml = null } } // Timeout checker if ( s.timeout > 0 ) { setTimeout(function(){ // Check to see if the request is still happening if( !requestDone ) uploadCallback( "timeout" ); }, s.timeout); } try { // var io = $('#' + frameId); var form = $('#' + formId); $(form).attr('action', s.url); $(form).attr('method', 'POST'); $(form).attr('target', frameId); if(form.encoding) { form.encoding = 'multipart/form-data'; } else { form.enctype = 'multipart/form-data'; } $(form).submit(); } catch(e) { jQuery.handleError(s, xml, null, e); } if(window.attachEvent){ document.getElementById(frameId).attachEvent('onload', uploadCallback); } else{ document.getElementById(frameId).addEventListener('load', uploadCallback, false); } return {abort: function () {}}; }, uploadHttpData: function( r, type ) { var data = !type; data = type == "xml" || data ? r.responseXML : r.responseText; // If the type is "script", eval it in global context if ( type == "script" ) jQuery.globalEval( data ); // Get the JavaScript object, if JSON is used. if ( type == "json" ) eval( "data = " + data ); // evaluate scripts within html if ( type == "html" ) jQuery("<div>").html(data).evalScripts(); //alert($('param', data).each(function(){alert($(this).attr('value'));})); return data; } }) But during my tesing, the code appears to run ok only in IE (IE 7). It failed on Firefox (3.0.10), Safari (3.2.1) and Chrome (1.0.154.65), with simply no request received from the server after “jQuery(form).submit();” and no exception thrown or captured either. I reported the problem back but so far no reply (I am not a Javascript expert). Hope your ajax file upload approach will also handle UTF8 encoded caption – I certainly would do some testing when your code is ready The other issue in my mind about file upload is if “multi-file upload in one submit” could be achieved. I saw it in Flash version where user selects multiple files using Window’s file selection while keeping control key down. I suppose this ability would bring Struts’ file upload to the next level. Kind regards, Qunhuan -----Original Message----- From: Wes Wannemacher [mailto:w...@wantii.com] Sent: 03 June 2009 22:03 To: Struts Users Mailing List Subject: Re: About the "struts.multipart.saveDir" for file upload I'll check it out, I just wanted to improve what we had because it's currently impossible to do things like an AJAX upload with accurate progress bar. After I get the plugin (1.0-SNAPSHOT, which is the maven way of saying beta) committed to the sandbox, I'll whip up a quick example of how to do an AJAX w/progress bar using struts. -Wes On Wed, Jun 3, 2009 at 5:00 PM, Andy <andrh...@hotmail.com> wrote: > > That's great Wes. I am using the file upload but only sparingly (and hacked) due to some existing glitches. > > > > I read how the Stripes framework does file upload: http://www.stripesframework.org/display/stripes/File+Uploads > > > > Thought you might find it interesting/useful.. > > > > Andy > > > >> Date: Wed, 3 Jun 2009 16:48:56 -0400 >> Subject: Re: About the "struts.multipart.saveDir" for file upload >> From: w...@wantii.com >> To: user@struts.apache.org >> >> I don't know if I would suggest it, but you might be able to call - >> >> Dispatcher.getInstance().setMultipartSaveDir(String path) >> >> Now, I can't remember if there is one dispatcher per thread, so you >> might have to call it in an interceptor. >> >> However, I would strongly suggest against doing that. >> >> I am working on an enhanced file upload plugin right now and will >> probably commit it to the sandbox by tomorrow. The problem with the >> stock file upload capability is that the FileItemFactory and >> FileUpload objects are not available to be overridden, and hardly any >> configuration is made visible. So, I've got a new >> JakartaMultiPartRequest class I'll have out by tomorrow. >> >> -Wes >> >> On Wed, Jun 3, 2009 at 4:37 PM, Andy <andrh...@hotmail.com> wrote: >> > >> > It defaults but I think he's wondering how he can set the dir programatically. >> > >> > >> > >> >> Date: Wed, 3 Jun 2009 15:35:33 -0500 >> >> Subject: Re: About the "struts.multipart.saveDir" for file upload >> >> From: burtonrho...@gmail.com >> >> To: user@struts.apache.org >> >> >> >> I wasn't aware that it was required. If you don't specify doesn't it >> >> default to the 'work' directory? >> >> >> >> On 6/3/09, Qunhuan Mei <q...@qm18.wanadoo.co.uk> wrote: >> >> > >> >> > Hi, >> >> > >> >> > When implementing file upload, it is required to specify an absolute >> >> > directory in struts.xml such as following: >> >> > >> >> > <constant name="struts.multipart.saveDir" value="C:/fileUploadTempDir" >> >> > /> >> >> > >> >> > But this might be difficult to achieve, say when the project war is deployed >> >> > in a external shared server and we do not have much idea about the directory >> >> > structure of the target server machine. >> >> > >> >> > I was wondering if there is any recommended way to handle this issue. >> >> > >> >> > Ideally I would like to specify the directory in java code where I can >> >> > detect the current working directory and specify the saveDir accordingly. Is >> >> > this possible? >> >> > >> >> > Many thanks in advance, >> >> > >> >> > Qunhuan >> >> > >> >> > >> >> > >> >> > >> >> > --------------------------------------------------------------------- >> >> > To unsubscribe, e-mail: user-unsubscr...@struts.apache.org >> >> > For additional commands, e-mail: user-h...@struts.apache.org >> >> > >> >> > >> >> >> >> -- >> >> Sent from my mobile device >> >> >> >> --------------------------------------------------------------------- >> >> To unsubscribe, e-mail: user-unsubscr...@struts.apache.org >> >> For additional commands, e-mail: user-h...@struts.apache.org >> >> >> > >> > _________________________________________________________________ >> > Windows Live™: Keep your life in sync. >> > http://windowslive.com/explore?ocid=TXT_TAGLM_WL_BR_life_in_synch_062009 >> >> >> >> -- >> Wes Wannemacher >> Author - Struts 2 In Practice >> Includes coverage of Struts 2.1, Spring, JPA, JQuery, Sitemesh and more >> http://www.manning.com/wannemacher >> >> --------------------------------------------------------------------- >> To unsubscribe, e-mail: user-unsubscr...@struts.apache.org >> For additional commands, e-mail: user-h...@struts.apache.org >> > > _________________________________________________________________ > Insert movie times and more without leaving Hotmail®. > http://windowslive.com/Tutorial/Hotmail/QuickAdd?ocid=TXT_TAGLM_WL_HM_Tutori al_QuickAdd_062009 -- Wes Wannemacher Author - Struts 2 In Practice Includes coverage of Struts 2.1, Spring, JPA, JQuery, Sitemesh and more http://www.manning.com/wannemacher --------------------------------------------------------------------- To unsubscribe, e-mail: user-unsubscr...@struts.apache.org For additional commands, e-mail: user-h...@struts.apache.org --------------------------------------------------------------------- To unsubscribe, e-mail: user-unsubscr...@struts.apache.org For additional commands, e-mail: user-h...@struts.apache.org