Ответ 1
С Webkit build r230963 эта проблема была решена в Webkit. Я загрузил и запустил эту сборку и подтвердил, что проблема решена. Не идея, когда публичный выпуск будет доступен для Safari, который содержит это исправление.
ОБНОВЛЕНИЕ: по версии Webkit r230963 эта проблема была решена в Webkit.
===========
Начиная с недавнего обновления Safari 11.1 на macOS и iOS, а также в Safari Technology Preview 11.2, вызовы $.ajax
в моем веб-приложении терпят неудачу, когда поле input[type=file]
не имеет выбранного файла (это не требуется в моей форме). Никакой сбой, если в поле есть выбранный файл.
Обратный вызов error
запуска ajax
и консоли Safari содержит следующее сообщение: Failed to load resource: The operation couldnt be completed. Protocol error
Failed to load resource: The operation couldnt be completed. Protocol error
. Я HTTPS и отправляю в место в том же домене (и сервере) также через HTTPS.
Перед обновлением 11.1 вызов $.ajax
передавался просто отлично, когда не был выбран файл. В последних версиях Chrome и Firefox нет проблем.
Соответствующие части моего кода:
Вход:
Browse... <input id="file-upload" type="file" name="image" accept=".jpg,.jpeg">
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 }
});
Как временное (надеюсь) решение, я обнаруживаю пустое поле файла и formData
его из formData
перед вызовом ajax
и все работает как ожидалось/раньше:
$("input[type=file]").each(function() {
if($(this).val() === "") {
formData.delete($(this).attr("name"));
}
});
Я что-то делаю неправильно, есть ли проблема с Safari, или есть изменения в Safari, которые нужно учитывать сейчас в вызовах ajax?
С Webkit build r230963 эта проблема была решена в Webkit. Я загрузил и запустил эту сборку и подтвердил, что проблема решена. Не идея, когда публичный выпуск будет доступен для Safari, который содержит это исправление.
UPDATE: старый ответ НЕ работает в Firefox.
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)
Демо-версия: 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.
Но порядок частей в 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
}
Пример Live здесь: https://jsfiddle.net/ypresto/y6v333bq/
Для Rails см. Здесь: https://github.com/rails/rails/issues/32440#issuecomment-381185380
(ПРИМЕЧАНИЕ: iOS 11.3 Safari имеет эту проблему, но 11.2 - нет.)
Для обходного пути я полностью удаляю файл типа ввода из DOM с помощью метода jQuery remove().
$("input[type=file]").each(function() {
if($(this).val() === "") {
$(this).remove();
}
});
Я работал над тем, что похоже на ту же проблему в Perl-программе
Обходное решение удаляет элементы формы до назначения formdata:
$('#myForm').find("input[type='file']").each(function(){
if ($(this).get(0).files.length === 0) {$(this).remove();}
});
var fData = new FormData($('#myForm')[0]);
...
var fileNames = formData.getAll("filename[]");
formData.delete("filename[]");
jQuery.each(fileNames, function (key, fileNameObject) {
if (fileNameObject.name) {
formData.append("filename[]", fileNameObject);
}
});
Попробуй это !!
Это работает для меня, чтобы проверить, пусто ли поле ввода. Если пусто, отключите поле ввода перед созданием FormData. После создания FormData удалите атрибут "disabled". Разница с другими ответами заключается в том, что я ищу "вход [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);