PDA

View Full Version : Callback before response in file upload



critchdizzle
11 May 2015, 11:42 AM
I'm investigating an intermittent issue where, after uploading a file, a new window opens with the JSON response from the server. I can't reproduce it in any of my local environments, though the client is reporting it quite a bit. In the callback function of my file upload process, the list of user files is refreshed. So there are two requests involved, one to upload the file, and one to get the file list. Most of the time, they are in order, and the timestamp on the second request is after that of the first response. However, I am seeing some cases (and the timestamps on the request match up with the times the user is experiencing the issue) where the file list request (i.e. the callback function) is firing off before the response for the file upload has returned from the server. Of course, if the callback is being fired, that means that the IFrame that handles the file upload has been destroyed, thus giving the response from the server for the file upload request nowhere to go but a new window. So the scenario is plausible, except for the callback occurring before the response has arrived from the server. I've attached a screencap of Fiddler showing one instance of this happening. The first request is the upload, and the second is the file refresh request that gets kicked off in the callback function. As you can see, the second request is being fired off before the response is received from the first request. Any ideas as to why this might be happening? This is the only client that is seeing this behavior, and it doesn't happen all the time.

Thanks in advance for your help, I know that's a lot of info so let me know if there are any questions.

tristan.lee
12 May 2015, 12:32 PM
Can you provide some code to go along with this upload process to help narrow it down?

critchdizzle
12 May 2015, 12:51 PM
Code in file uploader:



uploadFile:function(record, params) {
// fire beforestart event
if(true !== this.eventsSuspended && false === this.fireEvent('beforefilestart', this, record)) {
return;
}

// create form for upload
var form = this.createForm(record);

// append input to the form
var inp = record.get('input');
// Bootstrap.log('FileUploader.uploadFile | inp.dom.value = ' + inp.dom.value);
// inp.set({name: this.fileInputName ? this.fileInputName : inp.id});
inp.set({name: inp.id});
form.appendChild(inp);

// get params for request
var o = this.getOptions(record, params);
o.form = form;

// set state
record.set('state', 'uploading');
record.set('pctComplete', 0);

// increment active uploads count
this.upCount++;

// request upload
// Ext.Ajax.request(o);
Ext.ux.Ajax.request(o); // our custom Connection class

// todo:delete after devel
this.getIframe.defer(100, this, [record]);

} // eo function uploadFile
getOptions:function(record, params) {
// Having to supply the querystring with variables here
// because when pulling *anything* from the request.form object in C#,
// it causes IIS7 to upload the file in its entirity, and
// no progress tracking can be done.
// ARD 12-27-11
//
// Updated querystring params to include required fields.
// JDG 01-04-11
var input = record.get('input');
var fileName = input.dom.value.substring(input.dom.value.lastIndexOf('\\') + 1);
// Bootstrap.log('FileUploader.getOptions | filename: ' + fileName);
var url = this.url + '?UPLOAD_IDENTIFIER={uploadID}&MAX_FILE_SIZE={maxFileSize}&FILENAME={fileName}&userID={userID}&customerID={customerID}&documentSourceID={documentSourceID}&documentTypeID={documentTypeID}&comment={comment}';
url = util.TokenParser.parse(url, {
uploadID: record.data.progressId,
maxFileSize: this.maxFileSize,
fileName: fileName,
userID: record.get('userID'),
customerID: record.get('customerID'),
documentSourceID: record.get('documentSourceID'),
documentTypeID: record.get('documentTypeID'),
comment: record.get('comment') || ''
});
var o = {
url: url
,method:'post'
,isUpload:true
,scope:this
,callback:this.uploadCallback
,record:record
,params:this.getParams(record, params)
};
return o;
} // eo function getOptions

Overloaded doFormUpload method:



Ext.namespace('Ext.ux');


/**
* @class Ext.ux.Connection
* @extends Ext.data.Connection
*/

Ext.ux.Connection = Ext.extend(Ext.data.Connection, {
// private
doFormUpload : function(o, ps, url){
// Needed for Ext source locally scoped vars.
var BEFOREREQUEST = "beforerequest",
REQUESTCOMPLETE = "requestcomplete",
REQUESTEXCEPTION = "requestexception",
UNDEFINED = undefined,
LOAD = 'load',
POST = 'POST',
GET = 'GET',
WINDOW = window;

var id = Ext.id(),
doc = document,
frame = doc.createElement('iframe'),
form = Ext.getDom(o.form),
hiddens = [],
hd,
encoding = 'multipart/form-data',
buf = {
target: form.target,
method: form.method,
encoding: form.encoding,
enctype: form.enctype,
action: form.action
};

/*
* Originally this behaviour was modified for Opera 10 to apply the secure URL after
* the frame had been added to the document. It seems this has since been corrected in
* Opera so the behaviour has been reverted, the URL will be set before being added.
*/
Ext.fly(frame).set({
id: id,
name: id,
cls: 'x-hidden',
src: Ext.SSL_SECURE_URL
});

doc.body.appendChild(frame);

// This is required so that IE doesn't pop the response up in a new window.
if(Ext.isIE){
document.frames.name = id;
}


Ext.fly(form).set({
target: id,
method: POST,
enctype: encoding,
encoding: encoding,
action: url || buf.action
});

[I]// add dynamic params
Ext.iterate(Ext.urlDecode(ps, false), function(k, v){
hd = doc.createElement('input');
Ext.fly(hd).set({
type: 'hidden',
value: v,
name: k
});
form.appendChild(hd);
hiddens.push(hd);
});

function cb(){
var me = this,
// bogus response object
r = {responseText : '',
responseXML : null,
argument : o.argument},
doc,
firstChild;

try{
doc = frame.contentWindow.document || frame.contentDocument || WINDOW.frames.document;
if(doc){
if(doc.body){
if(/textarea/i.test((firstChild = doc.body.firstChild || {}).tagName)){ [I]// json response wrapped in textarea
r.responseText = firstChild.value;
}else{
r.responseText = doc.body.innerHTML;
}
}
//in IE the document may still have a body even if returns XML.
r.responseXML = doc.XMLDocument || doc;
}
}
catch(e) {}

Ext.EventManager.removeListener(frame, LOAD, cb, me);

me.fireEvent(REQUESTCOMPLETE, me, r, o);

function runCallback(fn, scope, args){
if(Ext.isFunction(fn)){
fn.apply(scope, args);
}
}

runCallback(o.success, o.scope, [r, o]);
runCallback(o.callback, o.scope, [o, true, r]);

if(!me.debugUploads){
setTimeout(function(){Ext.removeNode(frame);}, 100);
}
}

Ext.EventManager.on(frame, LOAD, cb, this);

function submitForm() {
form.submit();
frame.fireEvent('load');
}

try {
if (typeof form != 'undefined' && Ext.isDefined(form.submit)) {
submitForm();

} else {
Bootstrap.log('deferring submitForm()...');
submitForm.defer(1000);
}
} catch (ex) {
Bootstrap.log(ex.name + ' | ' + ex.message);

try {
Bootstrap.log('******** Trying again by deferring...');

submitForm.defer(2000);
} catch (ex2) {
Bootstrap.log(ex2.name + ' | ' + ex2.message);
}
}

Ext.fly(form).set(buf);
Ext.each(hiddens, function(h) {
Ext.removeNode(h);
});
}
});

Ext.ux.Ajax = new Ext.ux.Connection({
autoAbort : false,

serializeForm : function(form){
return Ext.lib.Ajax.serializeForm(form);
}
});

critchdizzle
12 May 2015, 12:57 PM
I just submitted some code to go along with the file upload issue we're having and just to prevent a wild goose chase, the frame.fireEvent('load') line is something I put in for testing. It wasn't present in the original code. Also, the src attribute of the frame was set to window.location for some reason (this is old code, and the person that wrote it is no longer here). Sorry, I didn't catch those before I sent it.

critchdizzle
12 May 2015, 12:59 PM
I know the callback is being fired by the 'load' event of the iframe that's being generated, I'm not sure what would be firing that event or loading something in there. Again, this is an intermittent issue reported by just one of our clients.

critchdizzle
18 May 2015, 8:20 AM
@tristan.lee any updates? I'm at the end of my rope on this one, not sure why it's doing it, and only doing it intermittently.

tristan.lee
18 May 2015, 10:00 AM
The only thing in question about that code is your submitForm() function that you're deferring. When these requests are made out of order, so you know if this method is being deferred or not? That's the only piece I can think of that might cause this without being able to reproduce it myself.

critchdizzle
18 May 2015, 10:33 AM
The defer should just delay the whole process, I'm not sure why (like I said the original developer is no longer here). The form is being submitted, and the callback is being executed before the response has returned from the server, which points to the load event of the iframe being fired prematurely. Exactly why that's happening, I'm not sure. The defer could be causing it, it's definitely something to check. We can't reproduce it either, but if you ask the client it's happening all the time. It might also be something client-side with respect to the performance of their machines, we know they do run programs that are memory hogs so the browser could be a victim of that. Thanks for taking a look, let me know if anything else comes to mind.

tristan.lee
19 May 2015, 6:06 AM
Have you had the client try it in different browsers by chance? I'm curious to know if that changes anything.

critchdizzle
19 May 2015, 6:17 AM
It happens in all 3 (Chrome, FF and IE). I was able to manually reproduce the behavior (i.e. by removing the iframe right after the form submit) that they're seeing in Chrome. The exact behavior with the code as-is, however, has never been able to be reproduced by us.