在Safari 10.13.4中发送包含空文件输入的FormData()时,AJAX请求失败

时间:2018-04-05 12:57:01

标签: javascript jquery ajax macos safari

我正在运行一个基于Symfony 2.8的Web应用程序,它使用Ajax将一些表单数据发送回控制器。

到目前为止一切工作正常,但自最新版本的10.13.4用户开始报告macOS更新后,提交表单在Safari中不再有效。 10.13.4上的其他macOS版本和其他浏览器仍然可以正常工作,因此它似乎是Safari中的一个问题。当然我向Apple提交了一份错误报告,但我不认为,我会从那里得到反馈......

我能够找出问题的根源:提交包含空文件输入的数据失败:

// 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";

结果

  • 提交表单适用于所有经过测试的浏览器和平台,但在macOS 10.13.4的Safari中
  • 在macOS 10.13.4上的Safari中:
    • 如果没有选择文件:Ajax请求运行大约20秒(构建超时?)而不是返回空的成功响应。调用submit.php NOT
    • 如果选择了一个文件:一切正常......

所以,这似乎是最新Safari更新中的一个错误?或者我的代码有什么问题吗?

知道如何防止这个错误吗?

5 个答案:

答案 0 :(得分:6)

Andrei Herford的解决方案会崩溃其他不支持FormData的entries()方法的浏览器 - 使用try / catch只会发现执行错误,而不会发现语法错误。

我们的解决方案是使用纯JavaScript在创建FormData对象之前删除空文件输入元素,因此:

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

答案 1 :(得分:6)

我在整个网站中使用FormData,并且可以验证这是最新版Safari的问题。删除空文件可以解决问题。这是适用于我的代码:

  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
  }

答案 2 :(得分:3)

我使用了这种解决方案并为我工作。

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 :(得分:1)

与此同时,我发现了这个快速而肮脏的解决方但实际上我正在寻找一个真正的解决方法。有什么想法吗?

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

答案 4 :(得分:0)

我使用了Andrei的建议,这个建议适用于safari,但打破了IE。

我能找到的唯一可以在两种浏览器中使用的解决方案是使用eval()。

由于这似乎只是影响safari 11的错误,我还添加了对浏览器版本的检查。

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