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

Mat*_*tt. 38 javascript forms safari ajax jquery

更新:从Webkit build r230963开始,此问题已在Webkit中得到解决.

===========

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

error回调ajax运行和Safari浏览器控制台包含以下信息:Failed to load resource: The operation couldn’t be completed. Protocol error.我是HTTPS并通过HTTPS提交到同一域(和服务器)上的某个位置.

在11.1更新之前,$.ajax当没有选择文件时,调用提交正常.最新版本的Chrome和Firefox没有任何问题.

我的代码的相关部分:

输入:

Browse... <input id="file-upload" type="file" name="image" accept=".jpg,.jpeg">
Run Code Online (Sandbox Code Playgroud)

JS:

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 }
});
Run Code Online (Sandbox Code Playgroud)

作为一个临时的(希望)解决方案,我正在检测一个空文件字段并formDataajax调用之前删除它,一切都按预期/之前工作:

$("input[type=file]").each(function() {
    if($(this).val() === "") {
        formData.delete($(this).attr("name"));
    }
});
Run Code Online (Sandbox Code Playgroud)

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

ypr*_*sto 18

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

火狐返回只是空字符串FormData.get()对空文件场(而不是File对象在其他浏览器).因此,当使用旧的解决方法时,空<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)
Run Code Online (Sandbox Code Playgroud)

现场演示: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')
})
Run Code Online (Sandbox Code Playgroud)

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


旧答案:

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

但是,根据规范,FormData中的部件顺序保证与输入表单中的元素顺序相同<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
}
Run Code Online (Sandbox Code Playgroud)

现场示例如下:https: //jsfiddle.net/ypresto/y6v333bq/

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

(注意:iOS 11.3的Safari有此问题,但11.2不是.)


man*_*007 5

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

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

  • 您可以在输入上添加"禁用"属性,因此不会在表单数据中收集它; 然后,您可以在要上载文件时删除该属性. (3认同)

Mat*_*tt. 5

Webkit build r230963开始,Webkit已经解决了这个问题.我下载并运行该构建并确认问题已解决.不知道哪个公共版本可用于包含此修复程序的Safari.