web-dev-qa-db-fra.com

jquery file upload - IE done callback data.result issue

J'utilise le fichier jQueryplugin de téléchargement .

Je n'utilise pas la partie UI, seulement la partie basique.

Lorsque je fais ce qui suit, j'ai un problème dans IE:

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

sur IE le data.result est un Object (IE9 au moins, pas sûr des autres ...)

Sur Chrome/Firefox est juste la réponse Text (simple texte brut du serveur).

Des idées?

30
CLod

Je viens de rencontrer le même problème, et je pense que cela est dû au fait que les téléchargements de fichiers XHR ne sont pas pris en charge dans IE (même 9). Voici ce que je crois qui se passe, et ma solution :

Étant donné que Safari 5+, Firefox 4+ et Chrome prend en charge les téléchargements de fichiers XHR, le plug-in fileupload peut transférer les fichiers de manière vraiment asynchrone, permettant une réponse en texte pur du serveur. Cette réponse en texte pur est disponible via data.result dans le gestionnaire d'événements done et peut être utilisé facilement.

Dans IE, cependant, le transfert de fichiers se produit via une actualisation de page complète dans un iframe caché, provoquant data.result dans le gestionnaire done pour être un objet document complet, avec le texte de réponse profondément enfoncé <html><body><iframe><pre> Mots clés.

Non seulement cela rend difficile l'accès aux données dans IE, mais la façon dont vous accédez aux données est différente d'un navigateur à l'autre.

Ma solution:

J'ai défini l'option forceIframeTransport sur true, ce qui oblige tous les navigateurs à transférer des fichiers avec une iframe cachée, comme IE. Il est malheureux de manquer les téléchargements de fichiers XHR, mais cela nous donne au moins le même type de réponse dans tous les navigateurs. Ensuite, dans mon gestionnaire done, j'ai extrait le résultat de l'objet document comme ceci:

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

Dans votre cas, je pense que le code ressemblerait à ceci:

$('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 );
    }
...

Il est également important de noter ici que le type de contenu de la réponse de mon serveur est 'text/plain'. Comme vous l'avez peut-être remarqué, IE invite parfois l'utilisateur à enregistrer ou à ouvrir une réponse json.

Voici quelques liens qui se sont révélés utiles pour résoudre le problème: https://github.com/blueimp/jQuery-File-Upload/wiki/Browser-support (voir la section 'XMLHttpRequest' en bas) https://github.com/blueimp/jQuery-File-Upload/wiki/Options (voir 'forceIframeTransport' environ 1/3 de la descente)

J'espère que cela aide!

50
Justin

J'ai rencontré des problèmes similaires à ceux mentionnés ci-dessus et j'ai constaté que le changement du type de réponse de application/json à text/plain a résolu ces problèmes.

13
Unknown

En fait, c'est ce que le jquery.iframe-transport.js est pour (la version de base inclut également ce plugin comme référence supplémentaire). Ce dont vous avez besoin est de définir la propriété dataType sur json et la jquery.iframe-transport le plugin analysera automatiquement le résultat iframe, vous pouvez donc avoir la même logique dans votre gestionnaire done.

$('#fileupload').fileupload({
...
    dataType: 'json'
...
});
5
Krisna Dharma

J'ai rencontré ce même problème. IE 9 ne déclencherait pas le rappel done, mais le ferait déclencherait le rappel always. La partie délicate consistait à extraire la réponse JSON (en dépit de ce que la documentation prétendait, je n'ai pas trouvé que je devais inclure jquery.iframe-transport.js.)

Mon implémentation du rappel always ressemblait à ceci:

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...
  }
}
4
Joey

Les réponses ici ont été très utiles pour comprendre et résoudre le problème. Je me suis retrouvé avec une solution satisfaisante mais piratée, qui permet aux navigateurs modernes de télécharger en utilisant XmlHttpRequest et IE9 d'utiliser iframe (n'a pas testé avec IE7/8):

  • Utilisation jquery.iframe-transport.js, il se charge d'extraire la réponse de l'iframe. Il suffit de l'inclure dans la page, le plugin fileupload l'utilisera.
  • Sur l'action du serveur utilisée pour télécharger les fichiers, renvoyez un résultat JSON, mais avec content-type mis à "text/plain", afin d'éviter IE invitant l'utilisateur à ouvrir le fichier
  • Dans le rappel terminé, évaluez manuellement le texte JSON
  • Enfin, utilisez l'objet résultat évalué comme précédemment

Voici le code JS:

//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...
        }
    }
}
1
Pierre Henry

Mon problème, la raison en est: Cross-Domain.

C'est-à-dire: dans IE8/9, l'url de votre serveur doit être dans le même domaine que votre fichier js. Pour jquery.iframe-transport.js est basé sur iframe, dans lequel vous ne pouvez pas inter-domaine. Vérifiez hehe

Vous n'avez pas besoin de définir forceIframeTransport sur true, car jquery.file-upload.js jugera le navigateur lui-même.

Votre serveur doit renvoyer Content-Type: text/html, ou peut-être text/plain, et vos js pourraient utiliser dataType: 'json', pas de problème. Au moins, le mien l'est.

Lorsque vous déboguez, vous pouvez définir un point d'arrêt dans jquery.iframe-transport.js à environ la ligne 85, le bloc try/catch. S'il peut se casser là-bas, cela signifie que iframe est utilisé et vous pouvez obtenir la réponse. À ce moment, vous pouvez voir votre json dans le DOM (dans iframe), mais si vous avez un domaine croisé, vous obtiendrez une réponse non définie.

Mon code. Pas de SPÉCIAL:

$('.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();
    }
});
0
NoteCode

Merci à @Justin pour une excellente explication à ce sujet. J'éviterais de faire des requêtes pour l'élément "pre" et ferais plutôt une solution simple comme commentée par @EnsignRicky

done: function (e, data) {
  var result = data.result[0].body ? data.result[0].body.innerHTML : data.result
}
0
Grantzau