Envoi de multipart / formdata avec jQuery.Ajax
j'ai un problème pour envoyer un fichier à un script PHP serverside en utilisant la fonction ajax de jQuery.
Il est possible d'obtenir la liste des fichiers avec $('#fileinput').attr('files')
mais comment envoyer ces données au serveur? Le tableau résultant ( $_POST
) sur le script php serverside est 0 ( NULL
) en utilisant le fichier-input.
je sais que c'est possible (bien que je n'ai pas trouvé de solutions jQuery jusqu'à maintenant, seulement Prototye code ( http://webreflection.blogspot.com/2009/03/safari-4-multiple-upload-with-progress.html )).
cela semble être relativement nouveau, alors s'il vous plaît ne pas mentionner que le téléchargement de fichier serait impossible via XHR/Ajax, parce que cela fonctionne certainement.
J'ai besoin de la fonctionnalité dans Safari 5, FF et Chrome serait agréable, mais ne sont pas essentiels.
mon code pour le moment est:
$.ajax({
url: 'php/upload.php',
data: $('#file').attr('files'),
cache: false,
contentType: 'multipart/form-data',
processData: false,
type: 'POST',
success: function(data){
alert(data);
}
});
11 réponses
à partir de Safari 5 / Firefox 4, il est plus facile d'utiliser la classe FormData
:
var data = new FormData();
jQuery.each(jQuery('#file')[0].files, function(i, file) {
data.append('file-'+i, file);
});
donc maintenant vous avez un objet FormData
, prêt à être envoyé avec 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);
}
});
il est impératif que vous positionniez l'option contentType
sur false
, forçant jQuery à ne pas ajouter d'en-tête Content-Type
pour vous, sinon, la chaîne de limite sera manquante.
Aussi, vous devez quitter le processData
drapeau mis à false, sinon, jQuery essaiera de convertir votre FormData
en une chaîne, qui échouera.
vous pouvez maintenant récupérer le fichier en PHP en utilisant:
$_FILES['file-0']
(il n'y a qu'un seul fichier, file-0
, sauf si vous avez spécifié l'attribut multiple
sur votre entrée de fichier, auquel cas, les nombres vont augmenter avec chaque fichier.)
utilisant le FormData emulation pour navigateurs plus anciens
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);
créer FormData à partir d'un formulaire existant
au lieu d'itérer manuellement les fichiers, L'objet FormData peut aussi être créé avec le contenu d'un objet form:
var data = new FormData(jQuery('form')[0]);
utiliser un tableau natif PHP au lieu d'un compteur
il suffit de nommer vos éléments de fichier les mêmes et de terminer le nom dans entre parenthèses:
jQuery.each(jQuery('#file')[0].files, function(i, file) {
data.append('file[]', file);
});
$_FILES['file']
sera alors un tableau contenant les champs d'upload de fichier pour chaque fichier téléchargé. En fait, je recommande cela par rapport à ma solution initiale car il est plus simple d'itérer plus.
voulait juste ajouter un peu à la grande réponse de Raphaël. Voici comment faire pour que PHP produise le même $_FILES
, peu importe si vous utilisez JavaScript pour soumettre.
format HTML:
<form enctype="multipart/form-data" action="/test.php"
method="post" class="putImages">
<input name="media[]" type="file" multiple/>
<input class="button" type="submit" alt="Upload" value="Upload" />
</form>
PHP produit ce $_FILES
, lorsqu'il est soumis sans 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 vous faites l'amélioration progressive, en utilisant le JS de Raphael pour soumettre les fichiers...
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);
}
});
... c'est ce à quoi ressemble le tableau $_FILES
de PHP, après avoir utilisé ce JavaScript pour soumettre:
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
)
)
C'est un joli tableau, et en fait ce que certaines personnes transforment $_FILES
En, mais je trouve qu'il est utile de travailler avec le même $_FILES
, peu importe si JavaScript a été utilisé pour soumettre. Ainsi, voici quelques changements mineurs à la 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 avril 2017 edit: j'ai supprimé l'élément de forme du constructeur de FormData () -- that fixe ce code dans Safari.)
ce code fait deux choses.
- récupère automatiquement l'attribut de nom
input
, ce qui rend le HTML plus maintenable. Maintenant, aussi longtemps queform
a la classe putImages, tout le reste est pris en charge automatiquement. C'est, leinput
pas besoin d'avoir de nom. - le format de tableau que le HTML normal soumet est recréé par le JavaScript dans le données.ajouter ligne. Notez les parenthèses.
avec ces modifications, soumettre avec JavaScript produit maintenant exactement le même tableau $_FILES
que soumettre avec HTML simple.
je viens de construire cette fonction basée sur des informations que j'ai lues.
utilisez-le comme si vous utilisiez .serialize()
, à la place mettez .serializefiles();
.
Je travaille ici dans mes tests.
//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);
Regardez mon code, il fait le travail pour moi
$( '#formId' )
.submit( function( e ) {
$.ajax( {
url: 'FormSubmitUrl',
type: 'POST',
data: new FormData( this ),
processData: false,
contentType: false
} );
e.preventDefault();
} );
si votre forme est définie dans votre HTML, il est plus facile de passer la forme dans le constructeur que d'itérer et ajouter des images.
$('#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 réponse de Devin Venable était proche de ce que je voulais, mais j'en voulais un qui fonctionnerait sur plusieurs formulaires, et utiliserait l'action déjà spécifiée dans le formulaire pour que chaque fichier aille au bon endroit.
je voulais aussi utiliser la méthode on() de jQuery pour éviter de l'utiliser .prêt.)(
qui m'a amené à ceci: (remplacer formSelector par votre sélecteur 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 classe FormData fonctionne, cependant dans iOS Safari (sur iPhone au moins) je n'ai pas pu utiliser la solution de Raphael Schweikert telle quelle.
Mozilla Dev a une belle page sur la manipulation des FormData objets .
ainsi, ajoutez un formulaire vide quelque part dans votre page, en spécifiant le type d'enctype:
<form enctype="multipart/form-data" method="post" name="fileinfo" id="fileinfo"></form>
ensuite, créer L'objet FormData comme:
var data = new FormData($("#fileinfo"));
et procéder comme dans code de Raphaël .
un gotcha que j'ai rencontré aujourd'hui je pense qu'il vaut la peine de souligner lié à ce problème: si l'url pour l'appel ajax est redirigé alors l'en-tête pour le type de contenu: 'multipart/form-data' peut être perdu.
par exemple, je postais sur http://server.com/context?param=x
dans L'onglet Réseau de Chrome j'ai vu l'en-tête multipart correct pour cette requête mais ensuite une redirection 302 à http://server.com/context/?param=x (noter la barre oblique après le contexte)
pendant la redirection l'en-tête multipart a été perdu. Assurer les demandes ne sont pas redirigés si ces solutions ne fonctionnent pas pour vous.
les versions plus anciennes D'IE ne prennent pas en charge FormData ( la liste complète de la prise en charge de FormData par le navigateur est ici: https://developer.mozilla.org/en-US/docs/Web/API/FormData ).
soit vous pouvez utiliser un plugin jquery (par exemple, http://malsup.com/jquery/form/#code-samples ) ou, vous pouvez utiliser une solution basée sur IFrame pour post multipart form data via ajax: https://developer.mozilla.org/en-US/docs/Learn/HTML/Forms/Sending_forms_through_JavaScript
toutes les solutions ci-dessus semblent bonnes et élégantes, mais L'objet FormData() n'attend aucun paramètre, mais utilise append() après l'instanciation, comme ce qu'on a écrit ci-dessus:
formData.annexe (val.nom, val.valeur);
- obtenez de l'objet de formulaire en jquery-> $("#id")[0]
- data = new FormData ($("#id") [0]);
- ok,les données que vous voulez