Envío de multipart / formdata con jQuery.ajax

Tengo un problema al enviar un archivo a una secuencia de comandos PHP en el servidor usando la función ajax de jQuery. Es posible obtener la lista de archivos con $('#fileinput').attr('files') pero ¿cómo es posible enviar estos datos al servidor? La matriz resultante ( $_POST ) en el servidor php-script es 0 ( NULL ) cuando se utiliza la entrada de archivo.

Sé que es posible (aunque hasta ahora no he encontrado ninguna solución jQuery, solo el código Prototye ( http://webreflection.blogspot.com/2009/03/safari-4-multiple-upload-with-progress.html ) )

Esto parece ser relativamente nuevo, así que no menciones que la carga de archivos sería imposible a través de XHR / Ajax, porque definitivamente está funcionando.

Necesito la funcionalidad en Safari 5, FF y Chrome sería bueno, pero no son esenciales.

Mi código por ahora es:

 $.ajax({ url: 'php/upload.php', data: $('#file').attr('files'), cache: false, contentType: 'multipart/form-data', processData: false, type: 'POST', success: function(data){ alert(data); } }); 

Comenzando con Safari 5 / Firefox 4, es más fácil usar la clase FormData :

 var data = new FormData(); jQuery.each(jQuery('#file')[0].files, function(i, file) { data.append('file-'+i, file); }); 

Entonces ahora tiene un objeto FormData , listo para ser enviado junto con XMLHttpRequest.

 jQuery.ajax({ url: 'php/upload.php', data: data, cache: false, contentType: false, processData: false, method: 'POST', type: 'POST', // For jQuery < 1.9 success: function(data){ alert(data); } }); 

Es imperativo que establezca la opción contentType en false , lo que obliga a jQuery a no agregar un encabezado Content-Type , de lo contrario, la cadena de límite faltará. Además, debe dejar el indicador processData establecido en falso, de lo contrario, jQuery intentará convertir su FormData en una cadena, que fallará.

Ahora puede recuperar el archivo en PHP usando:

 $_FILES['file-0'] 

(Solo hay un archivo, file-0 , a menos que haya especificado el atributo multiple en su entrada de archivo, en cuyo caso, los números boostán con cada archivo).

Uso de la emulación FormData para navegadores antiguos

 var opts = { url: 'php/upload.php', data: data, cache: false, contentType: false, processData: false, method: 'POST', type: 'POST', // For jQuery < 1.9 success: function(data){ alert(data); } }; if(data.fake) { // Make sure no text encoding stuff is done by xhr opts.xhr = function() { var xhr = jQuery.ajaxSettings.xhr(); xhr.send = xhr.sendAsBinary; return xhr; } opts.contentType = "multipart/form-data; boundary="+data.boundary; opts.data = data.toString(); } jQuery.ajax(opts); 

Crear FormData de una forma existente

En lugar de iterar manualmente los archivos, el objeto FormData también se puede crear con los contenidos de un objeto de formulario existente:

 var data = new FormData(jQuery('form')[0]); 

Use una matriz nativa de PHP en lugar de un contador

Simplemente asigne un nombre a sus elementos de archivo y finalice el nombre entre corchetes:

 jQuery.each(jQuery('#file')[0].files, function(i, file) { data.append('file[]', file); }); 

$_FILES['file'] será una matriz que contiene los campos de carga de archivos para cada archivo cargado. De hecho, recomiendo esto sobre mi solución inicial, ya que es más fácil de repetir.

Solo quería agregar un poco a la gran respuesta de Raphael. A continuación se $_FILES cómo hacer que PHP produzca los mismos $_FILES , independientemente de si usa JavaScript para enviar.

Forma HTML:

 

PHP produce este $_FILES , cuando se envía sin JavaScript:

 Array ( [media] => Array ( [name] => Array ( [0] => Galata_Tower.jpg [1] => 518f.jpg ) [type] => Array ( [0] => image/jpeg [1] => image/jpeg ) [tmp_name] => Array ( [0] => /tmp/phpIQaOYo [1] => /tmp/phpJQaOYo ) [error] => Array ( [0] => 0 [1] => 0 ) [size] => Array ( [0] => 258004 [1] => 127884 ) ) ) 

Si realizas una mejora progresiva, usando el JS de Raphael para enviar los archivos …

 var data = new FormData($('input[name^="media"]')); jQuery.each($('input[name^="media"]')[0].files, function(i, file) { data.append(i, file); }); $.ajax({ type: ppiFormMethod, data: data, url: ppiFormActionURL, cache: false, contentType: false, processData: false, success: function(data){ alert(data); } }); 

… así es como se ve la matriz $_FILES PHP, después de usar ese JavaScript para enviar:

 Array ( [0] => Array ( [name] => Galata_Tower.jpg [type] => image/jpeg [tmp_name] => /tmp/phpAQaOYo [error] => 0 [size] => 258004 ) [1] => Array ( [name] => 518f.jpg [type] => image/jpeg [tmp_name] => /tmp/phpBQaOYo [error] => 0 [size] => 127884 ) ) 

Es una buena matriz, y en realidad lo que algunas personas transforman $_FILES , pero me parece útil trabajar con los mismos $_FILES , independientemente de si se utilizó JavaScript para enviar. Entonces, aquí hay algunos cambios menores en JS:

 // match anything not a [ or ] regexp = /^[^[\]]+/; var fileInput = $('.putImages input[type="file"]'); var fileInputName = regexp.exec( fileInput.attr('name') ); // make files available var data = new FormData(); jQuery.each($(fileInput)[0].files, function(i, file) { data.append(fileInputName+'['+i+']', file); }); 

(14 de abril de 2017 editar: eliminé el elemento de formulario del constructor de FormData () – que arregló este código en Safari).

Ese código hace dos cosas.

  1. Recupera automáticamente el atributo de nombre de input , lo que hace que el HTML sea más fácil de mantener. Ahora, siempre que la form tenga la clase putImages, todo lo demás se cuidará automáticamente. Es decir, la input no necesita tener ningún nombre especial.
  2. El formato de matriz que el HTML normal envía es recreado por JavaScript en la línea data.append. Tenga en cuenta los corchetes.

Con estos cambios, el envío con JavaScript ahora produce precisamente la misma matriz $_FILES que la presentación con HTML simple.

Acabo de construir esta función en función de cierta información que leo.

Úselo como si usara .serialize() , en lugar de poner .serializefiles(); .
Trabajando aquí en mis pruebas.

 //USAGE: $("#form").serializefiles(); (function($) { $.fn.serializefiles = function() { var obj = $(this); /* ADD FILE TO PARAM AJAX */ var formData = new FormData(); $.each($(obj).find("input[type='file']"), function(i, tag) { $.each($(tag)[0].files, function(i, file) { formData.append(tag.name, file); }); }); var params = $(obj).serializeArray(); $.each(params, function (i, val) { formData.append(val.name, val.value); }); return formData; }; })(jQuery); 

Mira mi código, hace el trabajo por mí

 $( '#formId' ) .submit( function( e ) { $.ajax( { url: 'FormSubmitUrl', type: 'POST', data: new FormData( this ), processData: false, contentType: false } ); e.preventDefault(); } ); 

Si su formulario está definido en su HTML, es más fácil pasar el formulario al constructor que iterar y agregar imágenes.

 $('#my-form').submit( function(e) { e.preventDefault(); var data = new FormData(this); // <-- 'this' is your form element $.ajax({ url: '/my_URL/', data: data, cache: false, contentType: false, processData: false, type: 'POST', success: function(data){ ... 

La respuesta de Devin Venable fue cercana a lo que yo quería, pero quería una que funcionara en múltiples formas, y use la acción ya especificada en el formulario para que cada archivo vaya al lugar correcto.

También quería usar el método jQuery’s on () para evitar el uso de .ready ().

Eso me llevó a esto: (reemplace formSelector con su selector jQuery)

 $(document).on('submit', formSelecter, function( e ) { e.preventDefault(); $.ajax( { url: $(this).attr('action'), type: 'POST', data: new FormData( this ), processData: false, contentType: false }).done(function( data ) { //do stuff with the data you got back. }); }); 

La clase FormData funciona, sin embargo, en iOS Safari (al menos en el iPhone) no pude usar la solución de Raphael Schweikert como está.

Mozilla Dev tiene una buena página sobre la manipulación de objetos FormData .

Por lo tanto, agregue un formulario vacío en algún lugar de su página, especificando el enctype:

 

A continuación, cree el objeto FormData como:

 var data = new FormData($("#fileinfo")); 

y proceda como en el código de Raphael .

Uno de los que me encontré hoy, creo que vale la pena señalarlo relacionado con este problema: si se redirige la url para la llamada ajax, entonces se puede perder el encabezado para content-type: ‘multipart / form-data’.

Por ejemplo, estaba publicando en http://server.com/context?param=x

En la pestaña de red de Chrome, vi el encabezado de varias partes correcto para esta solicitud, pero luego un redireccionamiento 302 a http://server.com/context/?param=x (observe la barra después del contexto)

Durante la redirección, se perdió el encabezado de varias partes. Asegúrese de que las solicitudes no se redireccionen si estas soluciones no funcionan para usted.

Las versiones anteriores de IE no son compatibles con FormData (la lista de compatibilidad completa del navegador para FormData está aquí: https://developer.mozilla.org/en-US/docs/Web/API/FormData ).

O puede usar un plugin jquery (por ejemplo, http://malsup.com/jquery/form/#code-samples ) o puede usar la solución basada en IFrame para publicar datos de formularios de varias partes a través de ajax: https: // developer. mozilla.org/en-US/docs/Learn/HTML/Forms/Sending_forms_through_JavaScript

Todas las soluciones anteriores se ven bien y son elegantes, pero el objeto FormData () no espera ningún parámetro, pero usa append () luego de instanciarlo, como lo que se escribió arriba:

formData.append (val.name, val.value);

  1. obtener objeto de formulario por jquery-> $ (“# id”) [0]
  2. data = new FormData ($ (“# id”) [0]);
  3. ok, los datos son tu deseo