Mám problém s odoslaním súboru do PHP-skriptu na strane servera pomocou funkcie jQuery'ajax.
Je'možné získať File-List pomocou $('#fileinput').attr('files')
, ale ako je možné tieto dáta poslať na server? Výsledné pole ($_POST
) na strane servera php-skriptu je 0 (NULL
) pri použití file-input.
Viem, že je to možné (aj keď som doteraz nenašiel žiadne jQuery riešenie, iba Prototye kód (http://webreflection.blogspot.com/2009/03/safari-4-multiple-upload-with-progress.html)).
Zdá sa, že je to relatívne nové, takže prosím neuvádzajte, že by nahrávanie súborov nebolo možné cez XHR/Ajax, pretože to'určite funguje.
Potrebujem funkčnosť v Safari 5, FF a Chrome by boli fajn, ale nie sú nevyhnutné.
Môj kód je zatiaľ:
$.ajax({
url: 'php/upload.php',
data: $('#file').attr('files'),
cache: false,
contentType: 'multipart/form-data',
processData: false,
type: 'POST',
success: function(data){
alert(data);
}
});
Od prehliadača Safari 5/Firefox 4 je najjednoduchšie používať triedu FormData
:
var data = new FormData();
jQuery.each(jQuery('#file')[0].files, function(i, file) {
data.append('file-'+i, file);
});
Takže teraz máte objekt FormData
pripravený na odoslanie spolu s požiadavkou 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);
}
});
Je nevyhnutné, aby ste nastavili možnosť contentType
na false
, čím prinútite jQuery, aby za vás nepridávalo hlavičku Content-Type
, inak v nej bude chýbať ohraničujúci reťazec.
Taktiež musíte ponechať príznak processData
nastavený na false, inak sa jQuery pokúsi previesť vaše FormData
na reťazec, čo sa nepodarí.
Teraz môžete súbor načítať v jazyku PHP pomocou:
$_FILES['file-0']
(Existuje len jeden súbor, file-0
, pokiaľ ste nezadali atribút multiple
na vstupe súboru, v takom prípade sa budú čísla zvyšovať s každým súborom.)
Použitie emulácie FormData pre staršie prehliadače
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);
Vytvorenie FormData z existujúceho formulára
Namiesto ručného iterovania súborov možno objekt FormData vytvoriť aj z obsahu existujúceho objektu formulára:
var data = new FormData(jQuery('form')[0]);
Použite natívne pole PHP namiesto počítadla
Stačí pomenovať prvky súboru rovnako a názov ukončiť zátvorkami:
jQuery.each(jQuery('#file')[0].files, function(i, file) {
data.append('file[]', file);
});
$_FILES['file']
bude potom pole obsahujúce polia pre nahrávanie súborov pre každý nahraný súbor. V skutočnosti odporúčam toto riešenie namiesto môjho pôvodného, pretože je jednoduchšie iterovať.
Chcel som len doplniť skvelú odpoveď Raphaela. Tu'je návod, ako prinútiť PHP, aby vytváralo rovnaké $_FILES
bez ohľadu na to, či na odoslanie použijete JavaScript.
Formulár 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 vytvorí tento $_FILES
, ak je odoslaný bez JavaScriptu:
Jazyk: lang-none -->
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
)
)
)
Ak robíte postupné vylepšovanie pomocou Raphael's JS na odosielanie súborov...
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);
}
});
... takto vyzerá pole $_FILES
PHP po použití tohto JavaScriptu na odoslanie:
Jazyk: lang-none -->
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
)
)
To'je pekné pole a vlastne to, na čo niektorí ľudia transformujú $_FILES
, ale ja považujem za užitočné pracovať s rovnakým $_FILES
bez ohľadu na to, či bol na odoslanie použitý JavaScript. Takže tu je niekoľko drobných zmien v 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. apríla 2017 úprava: Odstránil som prvok formulára z konštruktora FormData() -- to opravilo tento kód v Safari.)
Tento kód robí dve veci.
input
name, vďaka čomu je HTML lepšie udržiavateľné. Teraz, pokiaľ má form
triedu putImages, o všetko ostatné sa postará automaticky. To znamená, že input
nemusí mať žiadne špeciálne meno.Po týchto zmenách teraz odosielanie pomocou JavaScriptu vytvára presne to isté pole $_FILES
ako odosielanie pomocou jednoduchého HTML.
Túto funkciu som vytvoril na základe informácií, ktoré som si prečítal.
Používajte ju podobne ako .serialize()
, namiesto toho len vložte .serializefiles();
.
Funguje tu v mojich testoch.
//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);