jquery file upload - IE done callback data.result issue
Asked Answered
S

8

30

I'm using jQuery file upload plugin.

I don't use the UI part, only basic one.

When I do the following, I'm having an issue in IE:

$('input#fileupload').fileupload({
    url: '/upload',
    done: function (e, data) {
    if(data.result != null && $.trim(data.result) != '')
        $('a#attachment').html(data.result);
    }
    //......................

on IE the data.result is an Object (IE9 at least, not sure the others...)

On Chrome/Firefox is just the response Text (simple plain text from the server).

Ideas?

Sarazen answered 11/1, 2012 at 4:16 Comment(1)
what server-side platform are you using and are you declaring the content type on your upload page?Lura
S
50

I just ran into the same problem, and I think it is due to the fact that XHR file uploads are not supported in IE (even 9). Here's what I believe is happening, and my solution:

Since Safari 5+, Firefox 4+, and Chrome support XHR file uploads, the fileupload plugin can transfer the files truly asynchronously, allowing for a pure text response from the server. This pure text response is available via data.result in the done event handler, and can be used easily.

In IE, however, the file transfer occurs via a full page refresh in a hidden iframe, causing data.result in the done handler to be a full document object, with the response text wrapped deep inside <html><body><iframe><pre> tags.

Not only does this make it a pain to get to the data in IE, it makes the way you get to the data different between browsers.

My solution:

I set the forceIframeTransport option to true, which makes all browsers transfer files with a hidden iframe, like IE. It's unfortunate to miss out on XHR file uploads, but this at least gives us the same kind of response in all browsers. Then, in my done handler, I extracted the result from the document object like so:

var result = $( 'pre', data.result ).text();

In your case, I think the code would look something like this:

$('input#fileupload').fileupload({
    forceIframeTransport: true,
    url: '/upload',
    done: function ( e, data ) {
     var result = $( 'pre', data.result ).text();
     if( result != null && $.trim( result ) != '' )
        $( 'a#attachment' ).html( result );
    }
...

Also important to note here is that the Content Type of the response from my server is 'text/plain'. As you may have noticed, IE sometimes prompts the user to save or open a json response.

Here are a couple links that proved useful when resolving the problem: https://github.com/blueimp/jQuery-File-Upload/wiki/Browser-support (see 'XMLHttpRequest' section at bottom) https://github.com/blueimp/jQuery-File-Upload/wiki/Options (see 'forceIframeTransport' about 1/3 of the way down)

Hopefully this helps!

Steinmetz answered 17/1, 2012 at 17:53 Comment(5)
Great answer - thanks. An alternative to switching off XHR uploads for all browsers is to test the result in the done method to get graceful degradation, eg: var html; if(data.result[0].body) { html = data.result[0].body.innerHTML; } else { html = data.result; }Cornelison
I am trying to make the plugin work in IE and getting the result as undefined in the done callback. any ideas?Inside
@EnsignRicky Thanks for the alternative answer. We had to go even deeper to get it to work data.result[0].body.childNodes[0].innerHTML;Vanwinkle
wasted so much time on work around for IE9, thanks for the fix.Sakti
when i set forceIframeTransport: true request for all browser changed and got Request.Files.Count=0. can you help me, please? (before this it doesnot work in ie9 only)Lizabethlizard
A
13

I had problems similar to the ones mentioned above and found that changing the response type from application/json to text/plain fixed those issues.

Adamsite answered 28/8, 2012 at 20:24 Comment(0)
U
5

Actually this is what the jquery.iframe-transport.js is for (the basic version also include this plugin as additional reference). What you need is to set the dataType property to json and the jquery.iframe-transport plugin will automatically parse the iframe result, so you can have the same logic in your done handler.

$('#fileupload').fileupload({
...
    dataType: 'json'
...
});
Uprush answered 23/10, 2013 at 9:37 Comment(0)
F
4

I ran into this same issue. IE 9 would not trigger the done callback, but would trigger the always callback. The tricky part was extracting the JSON response object from the arguments. (Despite what the documentation claimed, I did not find that I needed to include jquery.iframe-transport.js.)

My implementation of the always callback looked something like this:

always: function(e, data) {
  var result;
  if (data.textStatus == 'parsererror') {  // IE9 fails on upload's JSON response
    result = JSON.parse(data.jqXHR.responseText);
  } else if (data.textStatus == 'success') {
    result = data.result;
  }

  if (result) {
    // ...perform custom handling...
  }
}
Fathomless answered 19/4, 2012 at 18:27 Comment(2)
Darn, so close. The 'always' was helpful, but data.jqXHR.responseText is still undefined for me.Tarsal
"Despite what the documentation claimed, I did not find that I needed to include jquery.iframe-transport.js." yeah this is true for me as well (using version fileupload 7.2.1). if i include this file neither the "done" nor the "always" callback get triggered. if i leave it out at least the "always"-callback gets triggered so i left it out.Tankage
A
1

The answers here were very useful to understand and solve the problem. I ended up with a satisfactory if hacky solution, that allows modern browsers to upload using XmlHttpRequest, and IE9 to use iframe (didn't test with IE7/8) :

  • Use jquery.iframe-transport.js, it will take care of extracting the response from the iframe. Just include it in the page, the fileupload plugin will use it.
  • On the server action used to upload the files, return a JSON result, but with content-type set to "text/plain", in order to avoid IE prompting the user to open the file
  • In the done callback, manually evalutate the JSON text
  • Finally, use the evaluated result object as before

Here is the JS code :

//file upload
var upload = $(fileUploadElmtId).fileupload({

    dataType: 'text',  //if we use JSON IE9 will ask the user to open the file... see the FileUploadController class
    url: uploadUrl,
    autoUpload: true,

    done: function (e, data) {


        //parse the JSON string that was received from the server - since it was received as text/plain it is not automatically evaluated
        var result = $.parseJSON(data.result);

        $.each(result.files, function (index, file) {
            //do something with the result - in my case it is an array of uploaded files...
        }
    }
}
Attenweiler answered 22/11, 2013 at 15:8 Comment(1)
IE 7/8 still prompts to save the fileMartinmartina
S
0

Thanks to @Justin for a great explanation on this. I would avoid making queries for the "pre" element and instead make a simple solution like commented by @EnsignRicky

done: function (e, data) {
  var result = data.result[0].body ? data.result[0].body.innerHTML : data.result
}
Sargasso answered 2/7, 2013 at 12:11 Comment(0)
B
0

My problem, the reason is: Cross-Domain.

That is to say: in IE8/9, your server url should under the same domain as your js file. For jquery.iframe-transport.js is based on iframe, in which you can not cross-domain. Check hehe

You need not set the forceIframeTransport to true, for jquery.file-upload.js will judge the browser itself.

Your server should return Content-Type: text/html, or maybe text/plain, and your js could use dataType: 'json', no problem. At least, mine is.

When you debug, you could set breakpoint in jquery.iframe-transport.js at about line85, the try/catch block. If it can break there, that means iframe used, and you could get the response. At this time, you could see your json in the DOM(in iframe), but if you cross-domained, you would got response as undefined.

My code. No special:

$('.input-upload').fileupload({
    url: url, 
    dataType: 'json',
    autoUpload: false,
    acceptFileTypes: /(\.|\/)(gif|jpe?g|png)$/i,
    maxFileSize: 999000,
    previewMaxWidth: 114,
    previewMaxHeight: 70,
    previewThumbnail: false,
    xhrFields: {
        withCredentials: true
    }
}).on('fileuploadadd', function (e, data) {
    $.each(data.files, function (index, file) {
        data.submit().success(function (json, textStatus, jqXHR) {
            // on success
        }).error(function (jqXHR, textStatus, errorThrown) {
            // on error
        });
    });
}).on('fileuploadprocessalways', function (e, data) {
    var _this = this;
    var file = data.files[data.index];
    if (file.preview) {
        // 显示图片预览
        var preview = $(this).parent().siblings('.preview');
        preview.append(file.preview).removeHide();
    }
});
Berkeleian answered 23/1, 2017 at 8:41 Comment(0)
U
-2

I believe issue can be in server response, you can make sure that server always send correct heading by using following code:

header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
header("Last-Modified: " . gmdate("D, d M Y H:i:s") . "GMT");
header("Cache-Control: no-cache, must-revalidate");
header("Pragma: no-cache");
header("Content-type: text/x-json");
echo json_encode($array);
exit(0);
Underslung answered 11/1, 2012 at 4:23 Comment(5)
response-header:Response Headers Cache-Control max-age=0, private, must-revalidate Connection Keep-Alive Content-Length 15 Content-Type text/html; charset=utf-8Sarazen
I believe, you wrote your server response header, which says that content is presented as TEXT/HTML instead of TEXT/JSONUnderslung
dataType The type of data that is expected back from the server. Note: The UI version of the File Upload plugin sets this option to "json" by default. Type: string Example: 'json'Sarazen
@Clod if your problem solved, close your question by accepting answer.Underslung
if I render a json object with content type json, the 'done' callback seems to be not triggeredSarazen

© 2022 - 2024 — McMap. All rights reserved.