Αποστολή multipart/formdata με jQuery.ajax

Έχω ένα πρόβλημα με την αποστολή ενός αρχείου σε ένα PHP-script από την πλευρά του διακομιστή χρησιμοποιώντας την jQuery's ajax-function. Είναι δυνατόν να λάβω τη λίστα αρχείων με το $('#fileinput').attr('files'), αλλά πώς είναι δυνατόν να στείλω αυτά τα δεδομένα στον διακομιστή; Ο πίνακας που προκύπτει ($_POST) στο php-script από την πλευρά του διακομιστή είναι 0 (NULL) όταν χρησιμοποιείται το file-input.

Ξέρω ότι είναι δυνατό (αν και δεν βρήκα μέχρι τώρα λύσεις jQuery, μόνο κώδικα Prototye (http://webreflection.blogspot.com/2009/03/safari-4-multiple-upload-with-progress.html)).

Αυτό φαίνεται να είναι σχετικά καινούργιο, οπότε μην αναφέρετε ότι το ανέβασμα αρχείων θα ήταν αδύνατο μέσω XHR/Ajax, γιατί σίγουρα λειτουργεί'ς.

Χρειάζομαι τη λειτουργικότητα στο Safari 5, το FF και το Chrome θα ήταν ωραία αλλά δεν είναι απαραίτητα.

Ο κώδικάς μου προς το παρόν είναι: Ο κώδικας μου είναι ο εξής:

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

Ξεκινώντας από τον Safari 5/Firefox 4, είναι πιο εύκολο να χρησιμοποιήσετε την κλάση FormData:

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

Έτσι τώρα έχετε ένα αντικείμενο FormData, έτοιμο να σταλεί μαζί με το 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);
    }
});

Είναι απαραίτητο να ορίσετε την επιλογή contentType σε false, αναγκάζοντας την jQuery να μην προσθέσει μια επικεφαλίδα Content-Type για εσάς, διαφορετικά, η συμβολοσειρά ορίου θα λείπει από αυτήν. Επίσης, πρέπει να αφήσετε τη σημαία processData σε false, διαφορετικά, η jQuery θα προσπαθήσει να μετατρέψει τα FormData σας σε συμβολοσειρά, πράγμα που θα αποτύχει.

Μπορείτε τώρα να ανακτήσετε το αρχείο στην PHP χρησιμοποιώντας:

$_FILES['file-0']

(Υπάρχει μόνο ένα αρχείο, το file-0, εκτός αν έχετε καθορίσει το χαρακτηριστικό multiple στην είσοδο του αρχείου σας, οπότε οι αριθμοί θα αυξάνονται με κάθε αρχείο).

Χρησιμοποιώντας το FormData emulation για παλαιότερα προγράμματα περιήγησης

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);

Δημιουργία FormData από μια υπάρχουσα φόρμα

Αντί για χειροκίνητη επανάληψη των αρχείων, το αντικείμενο FormData μπορεί επίσης να δημιουργηθεί με τα περιεχόμενα ενός υπάρχοντος αντικειμένου φόρμας:

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

Χρησιμοποιήστε έναν εγγενή πίνακα της PHP αντί για έναν μετρητή

Απλά ονομάστε τα στοιχεία του αρχείου σας με τον ίδιο τρόπο και τελειώστε το όνομα σε παρένθεση:

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

$_FILES['file'] θα είναι τότε ένας πίνακας που θα περιέχει τα πεδία μεταφόρτωσης αρχείων για κάθε αρχείο που μεταφορτώνεται. Στην πραγματικότητα προτείνω αυτή τη λύση σε σχέση με την αρχική μου λύση, καθώς είναι απλούστερη στην επανάληψη.

Σχόλια (31)

Ήθελα απλώς να προσθέσω κάτι στη σπουδαία απάντηση του Raphael's. Να πώς μπορείτε να κάνετε την PHP να παράγει το ίδιο $_FILES, ανεξάρτητα από το αν χρησιμοποιείτε JavaScript για την υποβολή.

Φόρμα HTML:


   <input name="media[]" type="file" multiple/>
   <input class="button" type="submit" alt="Upload" value="Upload" />

Η PHP παράγει αυτό το $_FILES, όταν υποβάλλεται χωρίς JavaScript:

Σχόλια (1)

Μόλις έφτιαξα αυτή τη λειτουργία με βάση κάποιες πληροφορίες που διάβασα.

Χρησιμοποιήστε την όπως και τη χρήση της .serialize(), αντί να βάζετε απλά .serializefiles();.

Σχόλια (2)