PDA

View Full Version : File Upload Form Nightmare



cesarulo
23 May 2007, 1:04 PM
Hi,

this thread could be seen as a continuation of this guy (http://extjs.com/forum/showthread.php?t=6668), posted scarce hours ago.

In case you're reading, Animal, I followed your advice, and passed the el property of the Form object to the formUpdate call. For those you of who haven't, or don't care to, read my previous thread, here goes the whole code again, as is.

But before I paste it, I just want to say: can it be THIS hard to upload a file and hand the server's response over to a handler function? I've been to hell and back with this, and no one (save Animal, who's been very expedient with his responses) seems to either consider it a serious enough problem to merit an answer with the technical details, or simple enough to give me a link to a complete example that does just this.

Please don't misunderstand me. I AM a little frustrated, but not at the forum or its people. It's just that this seems to be a prevalent problem, yet nobody seems to care, so I'm confused. Am I the only one trying to do file uploads here? I'm just trying to understand, that's all. Any insights will be welcome.

Anyway, enough gibberish. Here goes the code:



function initFileUploadForm(scope){

var uploadForm = new Ext.form.Form({
//target script of the form action, baseParams will be passed in POST variables
labelAlign: 'right',
fileUpload: true,
baseParams: {path: scope.PWD}
});

//Description textarea
var descriptionField = new Ext.form.TextField({
allowBlank: false,
id: 'file_upload_description',
name: 'description',
fieldLabel: 'Description',
width: 300,
blankText: 'Please type a description'
});

//File upload input
var fileField = new Ext.form.TextField({
allowBlank: false,
id: 'upload_file',
inputType: 'file',
name: 'upload_file',
fieldLabel: 'File',
width: 300,
blankText: 'Please choose a file'
});

//define an UpdateManager so we can control the upload server script's response
scope.fileUploadUpdMgr = new Ext.UpdateManager(Ext.get('file_upload_messages'));

//add fields to form
uploadForm.add(descriptionField);
uploadForm.add(fileField);
uploadForm.addButton({
text: 'Upload',
handler: function(){
scope.fileUploadUpdMgr.formUpdate(uploadForm.el.dom, 'upload.php', true, scope.uploadResponse);
}
});
uploadForm.addButton({
text: 'Cancel',
handler: function(){scope.fileUploadDialog.hide()}
});

//add repsonse handlers
uploadForm.addListener('actioncomplete', scope.uploadSuccess, scope);
uploadForm.addListener('actionfailed', scope.uploadFailure, scope);

//render it
scope.fileUploadDialog.body.dom.innerHTML = '<div id="file_upload_dialog_body"></div>';
uploadForm.render('file_upload_dialog_body');

//hook the form up to the GUIContainer that called the function
scope.fileUploadForm = uploadForm;

}//function initFileUploadForm(scope)



function initFileUploadDialog(scope){
var dlg = new Ext.BasicDialog('file_upload_dialog', {
autoCreate: true,
height: 150,
width: 500,
minHeight: 100,
minWidth: 150,
modal: true,
shadow: true,
title: 'File Upload',
syncHeightBeforeShow: true
});
dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
scope.fileUploadDialog = dlg;
}//function initFileUploadDialog(scope)



The problem now? If you look at the previous post, Animal and I were discussing that you can't pass anything other than an HTMLElement to UpdateManager.formUpdate(). I told Animal that I can't pass that, because I don't have it: there's no API call in Form to return the HTML Element that underlies it.

Animal said I was right, and suggested two possible courses of action:

1) pass the .el property of the Form (not recommended, he clarified)
2) pass the id of the Form as a string.

I have not been able to give a shot to 2) because I don't see a Config option in the docs for an id, and I can't think of any other way to set or get it. Any help with this would be most welcome.

So I tried 1), and to my dismay, the server (PHP) reports that the $_FILES array is empty!! So I delved into the source code once more, and the problem is, once more, in formUpdate():



formUpdate : function(form, url, reset, callback){
if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
formEl = Ext.getDom(form);
if(typeof url == "function"){
url = url.call(this);
}
if(typeof params == "function"){
params = params();
}
url = url || formEl.action;
var cb = {
success: this.successDelegate,
failure: this.failureDelegate,
timeout: (this.timeout*1000),
argument: {"url": url, "form": formEl, "callback": callback, "reset": reset}
};
var isUpload = false;
var enctype = formEl.getAttribute("enctype");
if(enctype && enctype.toLowerCase() == "multipart/form-data"){
isUpload = true;
cb.upload = this.successDelegate;
}
this.transaction = Ext.lib.Ajax.formRequest(formEl, url, cb, null, isUpload, this.sslBlankUrl);
this.showLoading.defer(1, this);
}
},


this time, the



var enctype = formEl.getAttribute("enctype");
if(enctype && enctype.toLowerCase() == "multipart/form-data"){
isUpload = true;
cb.upload = this.successDelegate;
}


bit. enctype is returning as 'null'. The getAttribute() method IS defined, but it's not reflecting the fact that this IS a mulitpart/form-data form (though I *DID* set fileUpload to true upon constructor invocation, see above).

So, to sum this up: this is driving me around the bend. I just want to upload a form and be able to have full control over the upload script's response. Can anyone tell me one of:

1) How do I get/set the id for a from programmatically created with Form()?

or,

2) Can I somehow get an HTML Element that underlies that same form, which has all *relevant* properties set to the *right* values?

Sorry if I'm getting testy - it's my third day trying to do something that takes me 20 minutes to write in plain HTML and the learning curve is just turning a little bit cruel. I hope you understand.

Best,

Animal
23 May 2007, 11:42 PM
I assure you it's not that difficult. I have a file upload dialog which uses an Ext Form.

It's an Ext.form.BasicForm though created from markup, so I have access to the DOM form element.

You can specify the id in the config of your Form, and that's the id given to the Form's DOM form. If you don't pass it, Ext generates one, but you can pass your own.

You do know that you have to handle file uploads differently on your sever don't you? The POST body is not just a url encoded string of the form



param1=val1&param2=val2


It is a mime multipart message, http://en.wikipedia.org/wiki/MIME

I don't know if PHP handles this automatically, but I suspect not. Each parameter has it's own Content-Type.

I'm sure there are PHP utilities that you can use to read these. I have a Java MultipartMessage class which handles it, but that's no good for you PHP folks.

Animal
23 May 2007, 11:47 PM
OK, it won't create the form with the correct enctype to be a file upload, you'll have to specify an autoCreate param to the constructor:



var myForm = new Ext.form.Form({
autoCreate: {
tag: 'form',
id : "upload-form",
method : 'POST',
enctype : "multipart/form-data"
}
});

cesarulo
24 May 2007, 7:55 AM
OK, it won't create the form with the correct enctype to be a file upload, you'll have to specify an autoCreate param to the constructor:



var myForm = new Ext.form.Form({
autoCreate: {
tag: 'form',
id : "upload-form",
method : 'POST',
enctype : "multipart/form-data"
}
});



Hey Animal,

thanks a lot! I'm going to test this baby right away. I thought there had to be some obscure tweak that I was missing out on. I'll get back to you on how it went.

And about file uploads, of course I know I have to handle them differently *lol* I'm a web application developer (then again so many people go by that title that it's probably become meaningless these days).

That's why I say things like "the $_FILES array is empty" and "this takes me 20 minutes to do in plain HTML". Trust me on that one, I've done file uploads before :)

Okay Animal, I'll give your solution a test drive and see what comes out. It's great that you found me a solution with a Form, because I *did* try using the BasicForm generated from markup and I was having trouble rendering it in the BasicDialog.

Talk to you soon. Did I say thank you?

jboynton
14 Jun 2007, 6:04 AM
Couldn't get the autocreate method to work as posted by animal, but forcing the enctype in the handler for submission works:




var fileUploadUpdMgr = new Ext.UpdateManager(Ext.get('file-upload-messages'));
myForm.addButton({
text: 'Upload',
handler: function(){
var myEl = myForm.el.dom;
myEl.setAttribute("enctype","multipart/form-data");
fileUploadUpdMgr.formUpdate(myForm.el.dom, '/clips/uploadFile', true,uploadResponse);
}
});

cesarulo
14 Jun 2007, 7:16 AM
So you *did* run into the same problem as me?!?!?

YES! I'm not alone in the world!

Hey, thanks a lot! I'm in the middle of another project (also using Ext), but as soon as I'm done with it I'll give a shot to this and report here.

Thanks!

bscotf
27 Jun 2007, 7:48 AM
Thanks a ton! setAttribute worked perfectly for me.

I also could not get the autoCreate trick to insert the enctype attribute properly. Worked with both ff2 and ie7.

Kind Regards,

Scot

tdikarim
30 Aug 2007, 7:50 AM
Thanks a ton! setAttribute worked perfectly for me.

I also could not get the autoCreate trick to insert the enctype attribute properly. Worked with both ff2 and ie7.

Kind Regards,

Scot

Hi,

How do you get the response from the server ?
Because for some reason the file could'nt be store to the server.

Yhanks

trbot
19 Oct 2007, 10:35 PM
I've been searching for hours for a real solution, digging though ext source etc etc. The setAttribute hack right before I submit worked brilliantly. =)

Although this seems like a problem with the library, (probably something to do with how form enctype is set at creation-time) this should probably be the standard workaround.

If anyone cares to know, I implemented my file upload dialog in a Window > FormPanel using divs for each in html.

Also, for those of you working in Java / JSP / some servlet based technology, Jakarta File Upload is the easiest way to go. I'm up and running in 12 lines of code. (It allows access to form fields and form files, and differentiates between them simply.)

JasonMichael
15 Nov 2007, 1:26 PM
Animal, does this work with the latest stable version of Ext (1.1.x)? If I leave that "tag" property in the code, there is a major error... I take it out, then there's another error.. "stating that F has no properties"... seems like autocreate is broken.

blindauer
30 Jul 2008, 9:55 AM
FYI:

I got file upload working pretty quick using Ajax.request in EXT 2.1:


var newDocumentForm = new Ext.FormPanel({
labelWidth: 75,
frame:true,
id: 'newDocumentForm',
bodyStyle:'padding:5px 5px 0',
width: 350,
defaults: {width: 230},
defaultType: 'textfield',
items: [{
inputType: 'file',
name: 'file',
id: 'file',
fieldLabel: 'Select File'
}

]
});

var form = Ext.getCmp('newDocumentForm');
Ext.Ajax.request({url:'myurl',
success: this.completeCreateDocument,
form: form.getForm().getEl().dom,
isUpload: true,
headers: {'Content-type':'multipart/form-data'},
params: "someParam:value"
});

zab
1 Aug 2008, 12:43 AM
can you explain your code please? I have to do a form with Upload file and I don't understand very well your solution...

blindauer
4 Aug 2008, 4:34 AM
When you need to do file upload in EXT, you'll need to build a form using a 'file' input type:

...
[{
inputType: 'file',
name: 'file',
id: 'file',
fieldLabel: 'Select File'
}
...


When you go to submit that form, use the Ext.Ajax.request object/method to do the multi-part submission. You'd call this from an event handler of some sort. ('Save' button pressed, etc)

..

var form = Ext.getCmp('newDocumentForm');
Ext.Ajax.request({url:'myurl',
success: this.completeCreateDocument,
form: form.getForm().getEl().dom,
isUpload: true,
headers: {'Content-type':'multipart/form-data'},
params: "someParam:value"
});

...


'myUrl' is the URL that will handle the upload (JSP/ASP/etc...)

The key seems to be setting the content-type in the header to be multi-part. This will move the file for you and let you do something on the server side.

You'll need to write your own upload handler on the server, but that is outside of the scope of EXT.

pokerking400
16 Sep 2008, 6:37 AM
Most of the problems are happening in the server side. That is what i figured out.

If u use PHP as backened i can help as i implemented fileupload.:)