J'ai un problème pour envoyer un fichier à un script PHP côté serveur en utilisant la fonction ajax de jQuery . Il est possible d'obtenir la liste de fichiers avec $('#fileinput').attr('files')
, mais comment est-il possible d'envoyer ces données au serveur? Le tableau résultant ($_POST
) sur le script php côté serveur est 0 (NULL
) lors de l'utilisation de l'entrée de fichier.
Je sais que c'est possible (bien que je n'ai trouvé aucune solution jQuery jusqu'à présent, uniquement du code Prototye ( http://webreflection.blogspot.com/2009/03/safari-4-multiple-upload-with-progress. html )).
Cela semble être relativement nouveau, évitez donc de mentionner que le téléchargement de fichier serait impossible via XHR/Ajax, car cela fonctionne définitivement.
J'ai besoin de la fonctionnalité de Safari 5, FF et Chrome serait agréable mais pas indispensable.
Mon code pour l'instant 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);
}
});
À partir de Safari 5/Firefox 4, il est plus simple d’utiliser la classe FormData
:
var data = new FormData();
jQuery.each(jQuery('#file')[0].files, function(i, file) {
data.append('file-'+i, file);
});
Alors 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 de définir l'option contentType
sur false
, forçant jQuery à ne pas ajouter d'en-tête Content-Type
, sinon la chaîne de limite lui manquera . Vous devez également laisser l'indicateur processData
défini sur false, sinon, jQuery essaiera de convertir votre FormData
en une chaîne, ce qui échouera.
Vous pouvez maintenant récupérer le fichier dans PHP en utilisant:
$_FILES['file-0']
(Il n'y a qu'un seul fichier, file-0
, à moins que vous n'ayez spécifié l'attribut multiple
dans votre entrée de fichier, auquel cas les numéros seront incrémentés avec chaque fichier.)
Utilisation de l'émulation FormData pour les anciens navigateurs
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);
Create FormData à partir d'un formulaire existant
Au lieu d'itérer manuellement les fichiers, l'objet FormData peut également être créé avec le contenu d'un objet de formulaire existant:
var data = new FormData(jQuery('form')[0]);
Utilisez un tableau natif PHP au lieu d'un compteur
Nommez simplement les éléments de votre fichier et mettez le nom 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 de téléchargement de fichier pour chaque fichier téléchargé. En fait, je le recommande par rapport à ma solution initiale, car il est plus simple de le parcourir.
Je voulais juste ajouter un peu à l'excellente réponse de Raphaël. Voici comment obtenir PHP pour produire le même $_FILES
, que vous utilisiez JavaScript ou non.
Formulaire 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 une amélioration progressive, utilisez JS de Raphaël 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);
}
});
... voici à quoi ressemble le tableau $_FILES
de PHP, après avoir utilisé ce code JavaScript pour le 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 en $_FILES
, mais je trouve utile de travailler avec le même $_FILES
, que JavaScript ait été utilisé ou non. Alors, voici quelques changements mineurs au 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 formulaire du constructeur de FormData () - qui a corrigé ce code dans Safari.)
Ce code fait deux choses.
input
name, ce qui rend le code HTML plus facile à gérer. Maintenant, tant que form
a la classe putImages, tout le reste est traité automatiquement. C'est-à-dire que la input
n'a pas besoin de nom spécial.Avec ces modifications, la soumission avec JavaScript génère désormais exactement le même tableau $_FILES
que celui soumis avec du HTML simple.
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();
} );
Je viens de construire cette fonction en fonction des informations que j'ai lues.
Utilisez-le comme si vous utilisiez .serialize()
; mettez plutôt .serializefiles();
.
Travailler 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);
Si votre formulaire est défini dans votre code HTML, il est plus facile de le transmettre au constructeur que de le parcourir et de l'ajouter.
$('#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 je voulais un qui pourrait fonctionner sur plusieurs formulaires et utiliser l'action déjà spécifiée dans le formulaire afin que chaque fichier soit placé au bon endroit.
Je voulais aussi utiliser la méthode on () de jQuery afin d'éviter d'utiliser .ready ().
Cela m’a amené à ceci: (Remplacez 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, mais dans iOS Safari (du moins sur l'iPhone), je n'ai pas pu utiliser la solution de Raphael Schweikert telle quelle.
Mozilla Dev a une page Nice sur la manipulation d'objets FormData .
Alors, ajoutez un formulaire vide quelque part dans votre page, en spécifiant le type:
<form enctype="multipart/form-data" method="post" name="fileinfo" id="fileinfo"></form>
Ensuite, créez un objet FormData en tant que:
var data = new FormData($("#fileinfo"));
et procédez comme dans le code de Raphael .
Les anciennes versions de IE ne prennent pas en charge FormData (la liste complète de prise en charge du navigateur pour FormData 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 publier des données de formulaire multipart via ajax: https://developer.mozilla.org/en-US/docs/Learn/HTML/Forms/Sending_forms_through_JavaScript
Si l'entrée de fichier name
indique un tableau et des indicateurs multiple
, et que vous analysez l'intégralité de form
avec FormData
, il n'est pas nécessaire de procéder de manière itérative append()
à l'entrée des dossiers. FormData
gérera automatiquement plusieurs fichiers.
$('#submit_1').on('click', function() {
let data = new FormData($("#my_form")[0]);
$.ajax({
url: '/path/to/php_file',
type: 'POST',
data: data,
processData: false,
contentType: false,
success: function(r) {
console.log('success', r);
},
error: function(r) {
console.log('error', r);
}
});
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<form id="my_form">
<input type="file" name="multi_img_file[]" id="multi_img_file" accept=".gif,.jpg,.jpeg,.png,.svg" multiple="multiple" />
<button type="button" name="submit_1" id="submit_1">Not type='submit'</button>
</form>
Notez qu'un button type="button"
normal est utilisé, pas type="submit"
. Cela montre qu'il n'y a aucune dépendance à utiliser submit
pour obtenir cette fonctionnalité.
L'entrée résultante $_FILES
ressemble à ceci dans Chrome outils de développement:
multi_img_file:
error: (2) [0, 0]
name: (2) ["pic1.jpg", "pic2.jpg"]
size: (2) [1978036, 2446180]
tmp_name: (2) ["/tmp/phphnrdPz", "/tmp/phpBrGSZN"]
type: (2) ["image/jpeg", "image/jpeg"]
Remarque: dans certains cas, certaines images seront correctement téléchargées lorsqu’elles seront téléchargées en tant que fichier unique, mais elles échoueront lorsqu’elles seront téléchargées dans un ensemble de plusieurs fichiers. Le symptôme est que PHP signale $_POST
et $_FILES
vides sans que AJAX ne génère aucune erreur. Un problème survient avec Chrome 75.0.3770.100 et PHP 7.0. Cela ne semble se produire qu'avec 1 sur plusieurs dizaines d'images dans mon ensemble de test.
Jours Nova, vous n'avez même pas besoin de jQuery :) récupérer le tableau de support API
let result = fetch('url', {method: 'POST', body: new FormData(documemt.querySelector("#form"))})
Un problème que j’ai rencontré aujourd’hui mérite d’être signalé à propos de ce problème: si l’URL de l’appel ajax est redirigé, l’entête du type de contenu: 'multipart/form-data' peut être perdu.
Par exemple, je publiais sur http://server.com/context?param=x
Dans l'onglet Réseau de Chrome, j'ai trouvé l'en-tête multipart approprié pour cette demande, puis une redirection 302 vers http://server.com/context/?param=x (notez la barre oblique après le contexte).
Au cours de la redirection, l'en-tête multipart a été perdu. Assurez-vous que les demandes ne sont pas redirigées si ces solutions ne fonctionnent pas pour vous.
Toutes les solutions ci-dessus sont belles et élégantes, mais l'objet FormData () n'attend aucun paramètre, mais utilise append () après l'instancier, comme ce qui est écrit ci-dessus:
formData.append (val.name, val.value);