Ik'heb een probleem met het versturen van een bestand naar een serverside PHP-script met behulp van jQuery's ajax-functie.
Het is mogelijk om de File-List te krijgen met $('#fileinput').attr('files')
maar hoe is het mogelijk om deze Data naar de server te sturen? De resulterende array ($_POST
) op de serverside php-script is 0 (NULL
) bij gebruik van de file-input.
Ik weet dat het mogelijk is (hoewel ik tot nu toe geen jQuery oplossingen heb gevonden, alleen Prototye code (http://webreflection.blogspot.com/2009/03/safari-4-multiple-upload-with-progress.html)).
Dit schijnt relatief nieuw te zijn, dus zeg alsjeblieft niet dat file upload onmogelijk zou zijn via XHR/Ajax, want het'werkt wel degelijk.
Ik heb de functionaliteit nodig in Safari 5, FF en Chrome zouden leuk zijn maar zijn niet essentieel.
Mijn code voor nu is:
$.ajax({
url: 'php/upload.php',
data: $('#file').attr('files'),
cache: false,
contentType: 'multipart/form-data',
processData: false,
type: 'POST',
success: function(data){
alert(data);
}
});
Vanaf Safari 5/Firefox 4, is het het makkelijkst om de FormData
klasse te gebruiken:
var data = new FormData();
jQuery.each(jQuery('#file')[0].files, function(i, file) {
data.append('file-'+i, file);
});
Je hebt nu dus een FormData
object, klaar om meegestuurd te worden met het 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);
}
});
Het is noodzakelijk dat je de contentType
optie op false
zet, om jQuery te dwingen geen Content-Type
header voor je toe te voegen, anders zal de boundary string er in ontbreken.
Ook moet je de processData
flag op false laten staan, anders zal jQuery proberen om je FormData
om te zetten in een string, wat zal mislukken.
Je kunt nu het bestand in PHP ophalen met:
$_FILES['file-0']
(Er is slechts één bestand, file-0
, tenzij u het multiple
attribuut op uw bestandsinvoer hebt opgegeven, in welk geval de getallen met elk bestand zullen toenemen).
Gebruik de FormData emulatie voor oudere browsers
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);
Maak FormData van een bestaand formulier
In plaats van handmatig de bestanden te doorlopen, kan het FormData object ook aangemaakt worden met de inhoud van een bestaand formulier object:
var data = new FormData(jQuery('form')[0]);
Gebruik een PHP native array in plaats van een teller
Noem je bestandselementen gewoon hetzelfde en eindig de naam tussen haakjes:
jQuery.each(jQuery('#file')[0].files, function(i, file) {
data.append('file[]', file);
});
$_FILES['file']
zal dan een array zijn die de bestandsupload velden bevat voor elk geupload bestand. Ik raad dit eigenlijk aan boven mijn eerste oplossing, omdat het eenvoudiger is om te itereren.
Ik wilde alleen wat toevoegen aan Raphael's geweldige antwoord. Hier's hoe je PHP dezelfde $_FILES
kunt laten produceren, ongeacht of je JavaScript gebruikt om te verzenden.
HTML formulier:
<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 produceert deze $_FILES
, indien ingediend zonder 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
)
)
)
Als je progressieve verbetering doet, met Raphael's JS om de bestanden in te dienen...
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);
}
});
... zo ziet PHP's $_FILES
array er uit, na gebruik van dat JavaScript om te verzenden:
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
)
)
Dat's een mooie array, en eigenlijk waar sommige mensen $_FILES
in veranderen, maar ik vind het's handig om met dezelfde $_FILES
te werken, ongeacht of JavaScript is gebruikt om in te dienen. Dus, hier zijn wat kleine wijzigingen in de 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 april 2017 edit: ik heb het formulierelement verwijderd uit de constructor van FormData() -- dat heeft deze code in Safari opgelost).
Die code doet twee dingen.
input
naam attribuut automatisch op, waardoor de HTML beter onderhoudbaar wordt. Nu, zolang form
de class putImages heeft, wordt voor al het andere automatisch gezorgd. Dat wil zeggen, de input
hoeft geen speciale naam te hebben.Met deze veranderingen, produceert indienen met JavaScript nu precies dezelfde $_FILES
array als indienen met gewone HTML.
Ik heb deze functie gebouwd op basis van wat info die ik gelezen heb.
Gebruik het zoals het gebruik van .serialize()
, in plaats daarvan zet je gewoon .serializefiles();
.
Werken hier in mijn 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);