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

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

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

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

// safri_bug.html      
$(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); } }); }); }); // submit.php <?php echo "OK";

结果

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

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

知道如何防止这个bug?

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

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

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

我在整个网站中使用FormData,并且可以validation这是最新版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 } 

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

 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. 

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

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

我使用了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) {} }