83 votes

Utilisation des téléchargements de fichiers HTML5 avec AJAX et jQuery

Il est vrai qu'il existe des questions similaires sur Stack Overflow, mais il semble qu'aucune ne réponde à mes besoins.

Voici ce que je cherche à faire :

  • Le téléchargement d'une forme entière de données, dont un élément est une simple fichier
  • Travailler avec la bibliothèque de téléchargement de fichiers de Codeigniter

Jusqu'ici, tout va bien. Les données arrivent dans ma base de données comme j'en ai besoin. Mais j'aimerais aussi soumettre mon formulaire via un post AJAX :

  • Utilisation de l'API de fichier HTML5 native, et non de flash ou d'une solution iframe
  • De préférence, l'interface avec le système de bas niveau .ajax() Méthode jQuery

Je pense que je pourrais imaginer comment faire cela en téléchargeant automatiquement le fichier lorsque la valeur du champ change en utilisant du pur javascript, mais je préférerais faire tout cela en une seule fois sur for submit en jQuery. Je pense que ce n'est pas possible de le faire via des chaînes de requête car je dois passer l'objet fichier entier, mais je suis un peu perdu sur ce qu'il faut faire à ce stade.

Est-ce possible ?

91voto

clarkf Points 1683

Ce n'est pas trop difficile. Tout d'abord, jetez un coup d'œil à Interface FileReader .

Ainsi, lorsque le formulaire est soumis, il faut attraper le processus de soumission et

var file = document.getElementById('fileBox').files[0]; //Files[0] = 1st file
var reader = new FileReader();
reader.readAsText(file, 'UTF-8');
reader.onload = shipOff;
//reader.onloadstart = ...
//reader.onprogress = ... <-- Allows you to update a progress bar.
//reader.onabort = ...
//reader.onerror = ...
//reader.onloadend = ...

function shipOff(event) {
    var result = event.target.result;
    var fileName = document.getElementById('fileBox').files[0].name; //Should be 'picture.jpg'
    $.post('/myscript.php', { data: result, name: fileName }, continueSubmission);
}

Ensuite, du côté du serveur (c'est-à-dire myscript.php) :

$data = $_POST['data'];
$fileName = $_POST['name'];
$serverFile = time().$fileName;
$fp = fopen('/uploads/'.$serverFile,'w'); //Prepends timestamp to prevent overwriting
fwrite($fp, $data);
fclose($fp);
$returnData = array( "serverFile" => $serverFile );
echo json_encode($returnData);

Ou quelque chose comme ça. Je peux me tromper (et si c'est le cas, corrigez-moi s'il vous plaît), mais cela devrait stocker le fichier comme quelque chose comme 1287916771myPicture.jpg en /uploads/ sur votre serveur, et répondre avec une variable JSON (à un continueSubmission() ) contenant le nom du fichier sur le serveur.

Vérifiez fwrite() y jQuery.post() .

Sur la page ci-dessus, il est indiqué comment utiliser readAsBinaryString() , readAsDataUrl() y readAsArrayBuffer() pour vos autres besoins (par exemple, images, vidéos, etc.).

6voto

Gheljenor Points 51

Avec jQuery (et sans l'API FormData), vous pouvez utiliser quelque chose comme ceci :

function readFile(file){
   var loader = new FileReader();
   var def = $.Deferred(), promise = def.promise();

   //--- provide classic deferred interface
   loader.onload = function (e) { def.resolve(e.target.result); };
   loader.onprogress = loader.onloadstart = function (e) { def.notify(e); };
   loader.onerror = loader.onabort = function (e) { def.reject(e); };
   promise.abort = function () { return loader.abort.apply(loader, arguments); };

   loader.readAsBinaryString(file);

   return promise;
}

function upload(url, data){
    var def = $.Deferred(), promise = def.promise();
    var mul = buildMultipart(data);
    var req = $.ajax({
        url: url,
        data: mul.data,
        processData: false,
        type: "post",
        async: true,
        contentType: "multipart/form-data; boundary="+mul.bound,
        xhr: function() {
            var xhr = jQuery.ajaxSettings.xhr();
            if (xhr.upload) {

                xhr.upload.addEventListener('progress', function(event) {
                    var percent = 0;
                    var position = event.loaded || event.position; /*event.position is deprecated*/
                    var total = event.total;
                    if (event.lengthComputable) {
                        percent = Math.ceil(position / total * 100);
                        def.notify(percent);
                    }                    
                }, false);
            }
            return xhr;
        }
    });
    req.done(function(){ def.resolve.apply(def, arguments); })
       .fail(function(){ def.reject.apply(def, arguments); });

    promise.abort = function(){ return req.abort.apply(req, arguments); }

    return promise;
}

var buildMultipart = function(data){
    var key, crunks = [], bound = false;
    while (!bound) {
        bound = $.md5 ? $.md5(new Date().valueOf()) : (new Date().valueOf());
        for (key in data) if (~data[key].indexOf(bound)) { bound = false; continue; }
    }

    for (var key = 0, l = data.length; key < l; key++){
        if (typeof(data[key].value) !== "string") {
            crunks.push("--"+bound+"\r\n"+
                "Content-Disposition: form-data; name=\""+data[key].name+"\"; filename=\""+data[key].value[1]+"\"\r\n"+
                "Content-Type: application/octet-stream\r\n"+
                "Content-Transfer-Encoding: binary\r\n\r\n"+
                data[key].value[0]);
        }else{
            crunks.push("--"+bound+"\r\n"+
                "Content-Disposition: form-data; name=\""+data[key].name+"\"\r\n\r\n"+
                data[key].value);
        }
    }

    return {
        bound: bound,
        data: crunks.join("\r\n")+"\r\n--"+bound+"--"
    };
};

//----------
//---------- On submit form:
var form = $("form");
var $file = form.find("#file");
readFile($file[0].files[0]).done(function(fileData){
   var formData = form.find(":input:not('#file')").serializeArray();
   formData.file = [fileData, $file[0].files[0].name];
   upload(form.attr("action"), formData).done(function(){ alert("successfully uploaded!"); });
});

Avec l'API FormData, il vous suffit d'ajouter tous les champs de votre formulaire à l'objet FormData et de l'envoyer via $.ajax({ url : url, data : formData, processData : false, contentType : false, type : "POST"}).

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X