web-dev-qa-db-fra.com

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

Certes, il y a des questions similaires sur Stack Overflow, mais aucune ne semble répondre à mes besoins.

Voici ce que je cherche à faire:

  • Télécharger une forme complète de données, dont un élément est un fichier nique
  • Travailler avec la bibliothèque de téléchargement de fichiers de Codeigniter

Jusque là, tout va bien. Les données sont stockées dans ma base de données lorsque j'en ai besoin. Mais je voudrais aussi soumettre mon formulaire via un AJAX post:

  • Utilisation de l'API de fichier HTML5 native, pas de solution flash ou iframe
  • Interfaçage de préférence avec la méthode .ajax() jQuery de bas niveau

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 javascript pur, mais je préférerais tout faire d'un seul coup pour soumettre dans jQuery. Je pense que ce n'est pas possible de le faire via des chaînes de requête car je dois transmettre l'objet de fichier entier, mais je suis un peu perdu sur ce qu'il faut faire à ce stade.

Cela peut-il être réalisé?

82
Joshua Cody

Ce n'est pas trop dur Tout d'abord, jetez un oeil à FileReader Interface .

Ainsi, lorsque le formulaire est soumis, saisissez 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, côté 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 me trompe peut-être (et corrigez-moi si je suis, mais cela devrait stocker le fichier comme quelque chose comme 1287916771myPicture.jpg Dans /uploads/ Sur votre serveur, et répondre avec une variable JSON (à une fonction continueSubmission()]) contenant le nom de fichier sur le serveur.

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

Sur la page ci-dessus, il explique comment utiliser readAsBinaryString() , readAsDataUrl() et readAsArrayBuffer() pour vos autres besoins (images, vidéos, etc.).

91
clarkf

Avec jQuery (et sans 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, données: formData, processData: false, contentType: false, tapez: "POST"})

6
Gheljenor