问题
Please help guys, This one is a major BLOCKER!
I have a project that uses NodeJS + jQuery Form Plugin + Typescript in which I am trying to do a file upload. After the file upload the server sends a response to the POST message which renders on the screen. The file does get uploaded successfully and completely before the POST response renders on the screen. I expect the POST response to call the "success" function instead of having the page redirected to show the JSON response.
Here's the code:
$(new ID().setAsRoot().selectId()).append(
"<form id=\"fileUploadForm\" accept-charset=\"utf-8\" method=\"post\" action=\"/upload\" enctype=\"multipart/form-data\">" +
"<input id = \"filename\" type=\"file\" name=\"userfile\" multiple=\"multiple\" />" +
"<button type=\"submit\" id = \"submitButton\"> Upload </button></form>");
var form = $("#fileUploadForm");
form.submit(function (e) {
//e.preventDefault();
this.ajaxSubmit({
type: form.attr('method'),
url: form.attr('action'),
data: form.serialize(),
success: function (data) {
var x = JSON.parse(data);
alert("Success : " + x);
},
error: function (data) {
var x = JSON.parse(data);
alert("Error : " + x);
}
});
});
the success function does not get called (which means the alert message doesn't show). The JSON data just gets rendered on the screen like this:
{
"path": "data\\cb3409f1cc234ec3f64b60d80be13a3e.html",
"name": "feed.html"
}
There is an error on the console that says:
Uncaught SyntaxError: Unexpected token : shortcut_manager.js:123
(anonymous function) shortcut_manager.js:123
(anonymous function) extensions::messaging:327
Event.dispatchToListener extensions::event_bindings:386
Event.dispatch_ extensions::event_bindings:371
Event.dispatch extensions::event_bindings:392
dispatchOnMessage
Here's the server side code that handles it. The server uses NodeJS formidable module.
public upload(req:express.Request, res) {
var form = new formidable.IncomingForm();
var originalFileName:String;
var filePath:String;
form.uploadDir = this.directory;
form.keepExtensions = true;
form.type = 'multipart';
var fields = [];
form
.on("error", function (err) {
})
.on("field", function (field, value) {
})
.on("end", function () {
res.send({
path: filePath,
name: originalFileName
});
})
.on("file", function (name, file:formidable.File) {
originalFileName = file.name;
filePath = file.path;
});
form.parse(req);
return;
}
--Update--
If I do $.ajax
instead of this.ajax
. The file does not upload. The browser console shows an error:
XMLHttpRequest cannot load http://localhost/. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:8080' is therefore not allowed access.
回答1:
Based on your comment:
"Thanks for that suggestion. $.ajax does not load the at all. It shows an error which you may know of. Although your suspicion is right because, when I use "this.ajax" the file does get uploaded but the console shows an error "Type has no method ajax"
- You can't upload files using
$.ajax()
the same way you usually upload your form data. - Because of the error when you use
this.ajax
the form gets submitted. That's why you see the JSON response in the browser.
The question is where does this.ajax
come from? Did you copy the code from an example that uses a library you haven't included?
In order to upload files via AJAX you need some kind of plugin (doing it yourself is some effort, especially if you need support for older browsers).
Update
The submit handler should be as follows:
form.submit(function (e) {
e.preventDefault();
$(this).ajaxSubmit().done(function (data) {
var x = JSON.parse(data);
alert("Success : " + x);
}).fail(function (data) {
var x = JSON.parse(data);
alert("Error : " + x);
});
});
Explanation: Since you want to call a jQuery plugin you need a jQuery object, hence $(this)
. ajaxSubmit()
does not need any arguments (you can pass in options if you want, though). Because I didn't pass in arguments the callbacks are appended, in this case with done
and fail
(instead of success
and error
).
回答2:
Replace this.ajax("http://..." to url:form.attr('method')
var form = $("#fileUploadForm");
$.ajax({
type:form.attr('method'),
url:form.attr('action'),
data:form.serialize(),
success: function(data){
}});
回答3:
Are you asking why am I getting the error:
XMLHttpRequest cannot load localhost/. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'localhost:8080' is therefore not allowed access.
If so then what you need on your localhost server (assuming that is also Node) is a header set on the response from localhost
to allow the Origin of localhost:8080
to access this resource like this:
res.setHeader('Access-Control-Allow-Origin', 'http://localhost:8080');
Or perhaps you are actually trying to request from the same server but forgot that 8080 is your test server: if so then in your test you should list your URL as
$.ajax("http://localhost:8080", { //....
or to repeat the current origin:
$.ajax( window.location.origin,{ // ....
Your last alternative to avoiding this (if you can't set the Origin policies on the response) is to use a jsonp
service which is less strict but also less featured, but that's not appropriate for your task so I won't go into that.
Either way once you get past the Origin policy you'll have other hurdles as @zeroflagL mentions. Here's a method that might suit you: How to handle cross domain iframe file upload json response?
回答4:
I think there are several different issues with your client-side code.
I see that you define $form
but I never see where you define form
. You then call form.attr(...)
. Unless you have defined form
somewhere else this will immediately throw an error.
More importantly, standard $.ajax()
cannot upload files on older browsers (even as recent as IE 9!) The ability to do an XHR file upload was added in XHR2 (see CanIUse). If you intend for your web app to have compatibility with any down-level browsers you'll need to work around this limitation in some way.
The good news is that there are a variety of plugins that do most of the work for you. If you are using a version of jQuery greater than 1.6 I would recommend looking at the jQuery File Upload Plugin. It uses the new XHR file upload functionality when available and falls back on iFrame Transport in browsers that don't support the new hotness. While the iFrame Transport is simple and clever you don't really need to know anything about it, because the plugin abstracts it away. The plugin is completely free too.
The Basic Plugin will do the dirty work for you and the full featured version even has a built in UI. There's plenty of details on how to implement the server side methods as well. The basic syntax is super simple:
$("#fileUploadForm").fileupload()
If you're looking for something even lighter weight, the iFrame Transport Plugin may be all you need. (It's free as well.) I just implemented it in a project today. It's a single very well documented JS file. It enhances the standard functionality of the $.ajax()
function. The key is specifying iframe: true
in your list of AJAX options. Your ajax call would probably look something like this:
$.ajax("http://localhost", {
type: $form.attr('method'),
url: $form.attr('action'),
data: $form.serialize(),
dataType: "JSON",
iframe: true,
files: $("#MyFileInputId"),
success: function (data) {
alert("Success : " + data);
},
error: function (data) {
alert("Error : " + data);
}
});
On a side note, if you want to make extra sure the non-AJAX POST doesn't occur, you can add another line to your code:
$form.submit(function (e) {
e.preventDefault();
// do stuff
return false;
}
preventDefault()
will cancel the POST, even if an error occurs in the code that follows.
回答5:
var form = $("#fileUploadForm");
$.ajax({
type:form.attr('method'),
url:form.attr('action'),
dataType: 'json', // Expect a return value of json
data:form.serialize(),
success: function(data){
}});
来源:https://stackoverflow.com/questions/20729645/jquery-form-plugin-file-upload-using-post-redirects-to-post-response