web-dev-qa-db-fra.com

AJAX La requête échoue lors de l'envoi de FormData (), y compris une entrée de fichier vide dans Safari 10.13.4.

J'utilise une application Web basée sur Symfony 2.8 qui envoie des données de formulaire à un contrôleur utilisant Ajax.

Jusqu'à présent, tout fonctionnait bien, mais depuis la dernière mise à jour de macOS vers la version 10.13.4, les utilisateurs commencent à signaler que l'envoi du formulaire ne fonctionne plus dans Safari. Les autres versions de macOS et les autres navigateurs sous 10.13.4 fonctionnent toujours correctement. Il semble donc que ce soit un problème dans Safari. Bien sûr, j'ai déposé un rapport de bogue à Apple, mais je ne pense pas que je pourrai recevoir un retour de sa part ...

J'ai pu isoler la source du problème: La soumission de données contenant un fichier vide a échoué:

// safri_bug.html
<html>
    <head>
        <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
    </head>
<body>
    <form name="app_booking" method="post" action="/test/submit.php">
        <div><input type="text" id="someValue" name="value"></div>
        <div><input id="thefile" type="file" name="file"></div>
    </form>

    <button id="bSubmit" type="button">Submit</button>

    <script>    
        $(document).ready(function() {              
            $('#bSubmit').click(function() {
                var form = $('form');
                var data = new FormData(form[0]);

                $.ajax({
                    url : '/submit.php',
                    type : 'POST',
                    data : data,
                    contentType: false,
                    processData: false,
                    context : this,
                    success : function(response) {
                            alert('success: ' + response);
                    },
                    error: function (xhr, ajaxOptions, thrownError) {
                            alert('error: ' + xhr.responseText + ' - ' + thrownError);
                    }
                });
            });
        });
    </script>
</body>
</html>


// submit.php
<?php 
    echo "OK";

Résultat

  • La soumission du formulaire fonctionne correctement sur tous les navigateurs et plates-formes testés, mais dans Safari sous macOS 10.13.4.
  • Dans Safari sous macOS 10.13.4:
    • Si aucun fichier n'est sélectionné: la requête Ajax s'exécute pendant environ 20 secondes (délai d'inactivité?) Et renvoie avec une réponse de réussite vide. Le submit.php faitPASest appelé. 
    • Si un fichier a été sélectionné: tout fonctionne bien ...

Alors, cela semble être un bogue dans la dernière mise à jour de Safari? Ou y a-t-il un problème avec mon code?

Une idée de comment empêcher ce bug?

13
Andrei Herford

En attendant, j'ai trouvé cette solution rapide et sale. Mais en réalité, je cherche une solution réelle. Des idées?

// Filter out empty file just before the Ajax request
// Use try/catch since Safari < 10.13.4 does not support FormData.entries()
try {
   for (var pair of data.entries()) {
      if (pair[1] instanceof File && pair[1].name == '' && pair[1].size == 0)
         data.delete(pair[0]);  
   }
} catch(e) {}
1
Andrei Herford

La solution d'Andrei Herford plantera d'autres navigateurs qui ne prennent pas en charge la méthode entry () de FormData. L'utilisation de try/catch ne détectera que les erreurs d'exécution, pas les erreurs de syntaxe.

Notre solution consistait à utiliser du code JavaScript simple pour supprimer l'élément d'entrée de fichier vide avant créer l'objet FormData, ainsi:

for (i = 0; i < form.elements.length; i++) {
  if (form.elements[i].type == 'file') {
    if (form.elements[i].value == '') {
      form.elements[i].parentNode.removeChild(form.elements[i]);
    }
  }
}
6
lemonrock

J'utilise FormData sur l'ensemble de mon site et peux vérifier qu'il s'agit d'un problème avec la dernière version de Safari. Supprimer le fichier vide corrige le problème. Voici le code qui a fonctionné pour moi:

  var form = $('#formID');
  var data = new FormData(form[0])

  //hack to fix safari bug where upload fails if file input is empty
  if (document.getElementById("fileID").files.length == 0 ) { //if the file is empty
      data.delete('fileID'); //remove it from the upload data
  }
6
Leopold Mc

J'ai utilisé cette solution et travaille pour moi.

var $form = $('#website_settings_form');
var $inputs = $('input[type="file"]:not([disabled])', $form); //select input files
    $inputs.each(function(_, input) {
        if (input.files.length > 0) return 
        $(input).prop('disabled', true) //if the input doesn't have uploaded files will be disable
    })
    var formData = new FormData($form[0]);// create the form data
    $inputs.prop('disabled', false);//enable fields again.
3
Antonio Reyes

J'ai utilisé la suggestion d'Andrei, qui a fonctionné pour le safari, mais a cassé IE.

La seule solution que je pouvais trouver qui fonctionnerait dans les deux navigateurs était d'utiliser eval ().

Comme cela semble être un bogue affectant uniquement Safari 11, j'ai également ajouté une vérification de la version du navigateur.

if(dataObj instanceof FormData && navigator.userAgent.match(/version\/11((\.[0-9]*)*)? .*safari/i)) {
    try {
        eval('for (var pair of dataObj.entries()) {\
            if (pair[1] instanceof File && pair[1].name === \'\' && pair[1].size === 0) {\
                dataObj.delete(pair[0]);\
            }\
        }');
    } catch(e) {}
}
0
Andrew Primeau