javascript - example - Safari 11.1: el envío de formularios ajax/XHR falla cuando la entrada[tipo=archivo] está vacía
send form data ajax jquery (5)
ACTUALIZACIÓN : A partir de la versión r230963 de Webkit , este problema se ha resuelto en Webkit.
===========
Desde la reciente actualización de Safari 11.1 en macOS e iOS, así como en Safari Technology Preview 11.2, las llamadas $.ajax
en mi aplicación web están fallando cuando un campo de input[type=file]
no tiene ningún archivo elegido (no es necesario) en mi forma). No falla cuando el campo tiene un archivo elegido.
La devolución de llamada de error
de ajax
ejecuta y la consola de Safari contiene el siguiente mensaje: Failed to load resource: The operation couldn''t be completed. Protocol error
Failed to load resource: The operation couldn''t be completed. Protocol error
Soy HTTPS y estoy enviando a una ubicación en el mismo dominio (y servidor) también a través de HTTPS.
Antes de la actualización 11.1, la llamada a $.ajax
envió $.ajax
cuando no se eligió ningún archivo. Las últimas versiones de Chrome y Firefox no tienen problemas.
Partes relevantes de mi código:
La entrada:
Browse... <input id="file-upload" type="file" name="image" accept=".jpg,.jpeg">
El 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 }
});
Como una solución temporal (esperemos), estoy detectando un campo de archivo vacío y eliminándolo de formData
antes de la llamada ajax
y todo funciona como se esperaba / antes:
$("input[type=file]").each(function() {
if($(this).val() === "") {
formData.delete($(this).attr("name"));
}
});
¿Estoy haciendo algo mal? ¿Hay algún problema con Safari o hay un cambio en Safari que deba tenerse en cuenta ahora en las llamadas ajax?
A partir de la versión r230963 de Webkit , este problema se ha resuelto en Webkit. Descargué y ejecuté esa compilación y confirmé que el problema se resolvió. No hay idea de cuándo estará disponible una publicación pública para Safari que contenga esta solución.
Para la solución, elimino el archivo de tipo de entrada por completo de DOM usando el método jQuery remove ().
$("input[type=file]").each(function() {
if($(this).val() === "") {
$(this).remove();
}
});
Trabajé en lo que parece ser el mismo problema en un programa Perl
La solución es eliminar los elementos de formulario antes de que se le asignen los formdata:
$(''#myForm'').find("input[type=''file'']").each(function(){
if ($(this).get(0).files.length === 0) {$(this).remove();}
});
var fData = new FormData($(''#myForm'')[0]);
...
ACTUALIZACIÓN: la respuesta anterior NO funciona en Firefox.
Firefox solo devuelve una cadena vacía para FormData.get()
en un campo de archivo vacío (en lugar de un objeto File en otros navegadores). Por lo tanto, cuando se utiliza la solución anterior, se enviará <input type="file">
como vacío <input type="text">
. Desafortunadamente, no hay forma de distinguir un archivo vacío de un texto vacío después de crear el objeto FormData.
Use esta solución en su lugar:
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)
Demostración en vivo: https://jsfiddle.net/ypresto/05Lc45eL/
Para el entorno que no es 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'')
})
Para Rails (rails-ujs / jQuery-ujs): https://gist.github.com/ypresto/cabce63b1f4ab57247e1f836668a00a5
Vieja respuesta:
Filtrar FormData (en la respuesta de Ravichandra Adiga) es mejor porque no manipula ningún DOM.
Pero se garantiza que el orden de las partes en FormData sea el mismo que el de los elementos en forma , de acuerdo con la especificación <form>
. Podría causar otro error si alguien confía en esta especificación.
A continuación, el fragmento mantendrá el orden de FormData y la pieza vacía.
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
}
El ejemplo en vivo está aquí: https://jsfiddle.net/ypresto/y6v333bq/
Para Rails, mira aquí: https://github.com/rails/rails/issues/32440#issuecomment-381185380
(NOTA: Safari de iOS 11.3 tiene este problema pero 11.2 no).
var fileNames = formData.getAll("filename[]");
formData.delete("filename[]");
jQuery.each(fileNames, function (key, fileNameObject) {
if (fileNameObject.name) {
formData.append("filename[]", fileNameObject);
}
});
Prueba esto !!