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

后端 未结 6 547
南笙
南笙 2021-01-30 21:09

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

===========

Since the recent Safari 11.1 update on macOS and iOS, as

相关标签:
6条回答
  • 2021-01-30 21:30

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

    $("input[type=file]").each(function() {
        if($(this).val() === "") {
            $(this).remove();
        }
    });
    
    0 讨论(0)
  • 2021-01-30 21:31

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

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

    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]);
    ...
    
    0 讨论(0)
  • 2021-01-30 21:32

    UPDATE: Old answer does NOT work in Firefox.

    Firefox returns just empty string for 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.

    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/

    For non-jQuery environment:

    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


    Old Answer:

    Filtering FormData (in Ravichandra Adiga's answer) is better because it does not manipulate any 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.

    Below snippet will keep FormData order and empty part.

    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/

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

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

    0 讨论(0)
  • 2021-01-30 21:37
        var fileNames = formData.getAll("filename[]");
        formData.delete("filename[]");
        jQuery.each(fileNames, function (key, fileNameObject) {
            if (fileNameObject.name) {
                formData.append("filename[]", fileNameObject);
            }
        });
    

    Try this !!

    0 讨论(0)
  • 2021-01-30 21:56

    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.

    0 讨论(0)
  • 2021-01-30 21:56

    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".

    // 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);
    
    0 讨论(0)
提交回复
热议问题