Question

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. 
Was it helpful?

Solution

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 doneand fail (instead of success and error).

OTHER TIPS

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){
      }});

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?

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.

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){
      }});
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top