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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/49614091/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-29 08:39:26  来源:igfitidea点击:

Safari 11.1: ajax/XHR form submission fails when input[type=file] is empty

javascriptjqueryajaxformssafari

提问by Matt.

UPDATE: As of Webkit build r230963, this issue has been resolved in Webkit.

更新:自Webkit 版本 r230963 起,此问题已在 Webkit 中解决。

===========

============

Since the recent Safari 11.1 update on macOS and iOS, as well as in Safari Technology Preview 11.2, the $.ajaxcalls in my web application are failing when a input[type=file]field has no file chosen (it isn't required in my form). No failure when the field doeshave a file chosen.

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

The errorcallback of ajaxruns and the Safari console contains the following message: Failed to load resource: The operation couldn't be completed. Protocol error. I am HTTPS and submitting to a location on the same domain (and server) also over HTTPS.

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

Before the 11.1 update, the $.ajaxcall submitted just fine when no file was chosen. The latest versions of Chrome and Firefox have no issues.

在 11.1 更新之前,$.ajax当没有选择文件时,调用提交得很好。最新版本的 Chrome 和 Firefox 没有问题。

Relevant parts of my code:

我的代码的相关部分:

The input:

输入:

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

The JS:

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

As a temporary (hopefully) solution, I'm detecting an empty file field and removing it from formDatabefore the ajaxcall and everything works as expected/before:

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

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

Am I doing something wrong, is there an issue with Safari, or is there a change in Safari that needs to be accounted for now in ajax calls?

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

采纳答案by Matt.

As of Webkit build r230963, this issue has been resolved in Webkit. I downloaded and ran that build and confirmed the issue is resolved. Not idea when a public release will be available for Safari that contains this fix.

Webkit build r230963 起,此问题已在 Webkit 中解决。我下载并运行该构建并确认问题已解决。不知道什么时候可以为 Safari 提供包含此修复程序的公开版本。

回答by ypresto

UPDATE: Old answer does NOT work in Firefox.

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

Firefox returns just empty stringfor FormData.get()on empty file field (instead of File object in other browsers). So when using old workaround, empty <input type="file">will be sent like as empty <input type="text">. Unfortunately, there is no way to distinguish an empty file from an empty text after creating FormData object.

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

Use this solution instead:

请改用此解决方案:

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)

Live Demo: https://jsfiddle.net/ypresto/05Lc45eL/

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

For non-jQuery environment:

对于非 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')
})

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

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



Old Answer:

旧答案:

Filtering FormData (in Ravichandra Adiga's answer) is better because it does not manipulate any DOM.

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

But the order of parts in FormData is guaranteed to be the same order to input elements in form, according to <form>specification. It could cause another bug if someone relies on this spec.

但是,根据规范,FormData 中各部分的顺序保证与 form 中输入元素的顺序相同<form>。如果有人依赖此规范,则可能会导致另一个错误。

Below snippet will keep FormData order and empty part.

下面的代码段将保留 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
}

Live example is here: https://jsfiddle.net/ypresto/y6v333bq/

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

For Rails, see here: https://github.com/rails/rails/issues/32440#issuecomment-381185380

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

(NOTE: iOS 11.3's Safari has this issue but 11.2 is not.)

(注意:iOS 11.3 的 Safari 有这个问题,但 11.2 没有。)

回答by mani_007

For workaround I delete the input type file completely from DOM using jQuery remove() method.

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

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

回答by Roger Van Montfort

I worked on what appears to be the same issue in a Perl-program

我在 Perl 程序中处理了似乎相同的问题

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

当表单文件元素为空时,在 Perl 中处理多部分/表单数据会调用 Apple 设备的 Apache 错误

Workaround is removing the form-elements before the formdata is assigned:

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

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

回答by Ravichandra Adiga

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

Try this !!

试试这个 !!

回答by Martin

This works for me to check if the input field is empty. If empty, disable the input field before created the FormData. After created the FormData, remove the "disabled" attribute. The difference to other answers is, that I search for "input[0].files.length == 0".

这对我有用,可以检查输入字段是否为空。如果为空,则在创建 FormData 之前禁用输入字段。创建 FormData 后,删除“禁用”属性。与其他答案的不同之处在于,我搜索“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);