Safari 11.1:当输入[type = file]为空时,ajax / XHR表单提交失败

时间:2018-04-02 15:32:31

标签: javascript jquery ajax forms safari

更新:自Webkit build r230963起,此问题已在Webkit中得到解决。

===========

自从最近在macOS和iOS上以及Safari Technology Preview 11.2中进行Safari 11.1更新以来,当$.ajax字段没有选择文件时,我的Web应用程序中的input[type=file]调用失败了我的表格中没有要求)。当 字段选择了文件时没有失败。

error的{​​{1}}回调运行,Safari控制台包含以下消息:ajax。我是HTTPS并通过HTTPS提交到同一域(和服务器)上的某个位置。

在11.1更新之前,Failed to load resource: The operation couldn’t be completed. Protocol error调用在没有选择文件时提交正常。最新版本的Chrome和Firefox都没有问题。

我的代码的相关部分:

输入:

$.ajax

JS:

Browse... <input id="file-upload" type="file" name="image" accept=".jpg,.jpeg">

作为一种临时(希望)解决方案,我检测到一个空文件字段,并在var formData = new FormData($(this)[0]); $.ajax({ type: 'POST', enctype: 'multipart/form-data', url: '../process.php', data: formData, contentType: false, processData: false, cache: false, success: function(response) { ... }, error: function() { //my code reaches here } }); 调用之前将其从formData中删除,并且所有内容都按预期/之前工作:

ajax

我做错了什么,Safari是否有问题,或者现在在ajax调用中是否需要考虑Safari的变化?

6 个答案:

答案 0 :(得分:18)

更新:旧答案在Firefox中不起作用。

Firefox在空文件字段(而不是其他浏览器中的File对象)上为FormData.get()返回空字符串。因此,在使用旧的解决方法时,空<input type="file">将像空<input type="text">一样发送。不幸的是,在创建FormData对象后,无法区分空文件和空文本。

请改用此解决方案:

var $form = $('form')
var $inputs = $('input[type="file"]:not([disabled])', $form)
$inputs.each(function(_, input) {
  if (input.files.length > 0) return
  $(input).prop('disabled', true)
})
var formData = new FormData($form[0])
$inputs.prop('disabled', false)

现场演示:https://jsfiddle.net/ypresto/05Lc45eL/

对于非jQuery环境:

var form = document.querySelector('form')
var inputs = form.querySelectorAll('input[type="file"]:not([disabled])')
inputs.forEach(function(input) {
  if (input.files.length > 0) return
  input.setAttribute('disabled', '')
})
var formData = new FormData(form)
inputs.forEach(function(input) {
  input.removeAttribute('disabled')
})

对于Rails(rails-ujs / jQuery-ujs):https://gist.github.com/ypresto/cabce63b1f4ab57247e1f836668a00a5

旧答案:

过滤FormData(在Ravichandra Adiga的答案中)更好,因为它不会操纵任何DOM。

但是the order of parts in FormData is guaranteed to be the same order to input elements in form,根据<form>规范。如果有人依赖这个规范,它可能会导致另一个错误。

下面的代码段将保留FormData订单和空白部分。

var formDataFilter = function(formData) {
    // Replace empty File with empty Blob.
  if (!(formData instanceof window.FormData)) return
  if (!formData.keys) return // unsupported browser
  var newFormData = new window.FormData()
  Array.from(formData.entries()).forEach(function(entry) {
    var value = entry[1]
    if (value instanceof window.File && value.name === '' && value.size === 0) {
      newFormData.append(entry[0], new window.Blob(), '')
    } else {
      newFormData.append(entry[0], value)
    }
  })
  return newFormData
}

现场示例在这里: https://jsfiddle.net/ypresto/y6v333bq/

对于Rails,请参见此处:https://github.com/rails/rails/issues/32440#issuecomment-381185380

(注意:iOS 11.3&Safari的Safari存在此问题,但11.2不存在。)

答案 1 :(得分:5)

对于解决方法,我使用jQuery remove()方法从DOM中完全删除输入类型文件。

$("input[type=file]").each(function() {
    if($(this).val() === "") {
        $(this).remove();
    }
});

答案 2 :(得分:5)

Webkit build r230963起,此问题已在Webkit中得到解决。我下载并运行该构建并确认问题已解决。不知道哪个公共版本可用于包含此修复程序的Safari。

答案 3 :(得分:2)

我参与了Perl程序中似乎相同的问题

Processing multipart/form-data in Perl invokes Apache-error with Apple-devices, when a form-file-element is empty

解决方法是在分配formdata之前删除表单元素:

$('#myForm').find("input[type='file']").each(function(){
    if ($(this).get(0).files.length === 0) {$(this).remove();}
});
var fData = new FormData($('#myForm')[0]);
...

答案 4 :(得分:1)

    var fileNames = formData.getAll("filename[]");
    formData.delete("filename[]");
    jQuery.each(fileNames, function (key, fileNameObject) {
        if (fileNameObject.name) {
            formData.append("filename[]", fileNameObject);
        }
    });

试试这个!!

答案 5 :(得分:0)

这对我有用,可以检查输入字段是否为空。如果为空,请在创建FormData之前禁用输入字段。创建FormData之后,删除“ disabled”属性。与其他答案的区别在于,我搜索“ input [0] .files.length == 0”。

// get the input field with type="file"
var input = $('#myForm').find("input[type='file']")

// add the "disabled" attribute to the input
if (input[0].files.length == 0) {
  input.prop('disabled', true);
}

// create the formdata  
var formData = new FormData($(this)[0]);

// remove the "disabled" attribute
input.prop('disabled', false);