2010-10-23 7 views
77

Certes, il existe des questions similaires sur Stack Overflow, mais il semble que rien ne corresponde à mes exigences.Utilisation de téléversements de fichiers HTML5 avec AJAX et jQuery

Voici ce que je cherche à faire:

  • Télécharger un formulaire complet de données, une pièce dont est un unique fichier
  • travail avec la bibliothèque de téléchargement de fichiers Codeigniter

Jusqu'à ici, 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 poste AJAX:

  • Utilisation de l'HTML5 API de fichier natif, pas flash ou une solution iframe
  • interface De préférence avec le faible niveau .ajax() méthode jQuery

Je pense que je pourrais imaginer comment le faire en téléchargeant automatiquement le fichier lorsque la valeur du champ change en utilisant le javascript pur, mais je préfère le faire d'un seul coup pour soumettre dans jQuery. Je pense qu'il n'est pas possible de faire par le biais de 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 que cela peut être réalisé?

+0

Je n'ai aucune idée de la partie Codeigniter, mais pour la partie jQuery, recherchez [ce plugin] (http://code.google.com/p/jquery-html5-upload/). – BalusC

+3

connexes: http://stackoverflow.com/questions/166221/how-can-i-upload-files-asynchronously-with-jquery –

Répondre

85

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

Ainsi, lorsque le formulaire est soumis, attrapez 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); 
} 

Puis, du côté du serveur (à savoir 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 je suis, s'il vous plaît, corrigez-moi), mais cela devrait stocker le fichier 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 .

Consultez 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.).

+0

Hey Clark, je comprends bien?Cela envoie le fichier téléchargé dès qu'il est chargé dans le constructeur FileReader à partir du système de fichiers, en contournant le gestionnaire .ajax de bas niveau de jQuery. Ensuite, le reste du formulaire sera soumis comme d'habitude? –

+0

Très bien, donc j'avais tort dans ma compréhension avant. Maintenant je prends le readAsDataUrl d'une image, l'ajoutant à mon datastring dans .ajax, et soumettant toutes mes informations ensemble. Ma précédente solution impliquait la classe d'entrée de fichier par défaut de CodeIgniter qui saisissait les données de $ _FILES ['field'], il semble donc que je devrais passer à une autre solution pour analyser les données de l'image base64. Tout conseil à ce sujet est le bienvenu, ce qui va augmenter votre réponse ici, et une fois que j'aurai terminé la mise en œuvre, je l'indiquerai comme correct. –

+1

@Joshua Cody - J'ai mis à jour la réponse pour donner un peu plus de détails. Vous devrez pardonner que je n'ai pas utilisé CodeIgniter dans beaucoup de lunes et je ne pourrais pas vous dire comment l'intégrer dans leur base de code. Je ne sais pas pourquoi vous devez télécharger le fichier avant de le soumettre, mais cela devrait au moins vous donner un indice. (Vous pouvez également insérer l'image dans une base de données si c'est mieux pour vous). – clarkf

6

Avec jQuery (et sans FormData API), 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 API FormData il vous suffit d'ajouter tous les champs de votre formulaire pour objet FormData et l'envoyer par .ajax $ ({url: url, données: formData, processData: false, contentType: false, tapez: "POST"})

+0

Cette solution n'aborde pas la limitation que XMLHttpRequest.send() impose aux données qui y sont acheminées. Lorsqu'une chaîne est passée (comme votre multipart), send() ne supporte pas les données binaires. Votre multipart ici sera traité comme une chaîne utf-8, et étouffera ou corrompra les données binaires qui ne sont pas utf-8 valides. Si vous devez vraiment éviter FormData, vous devez utiliser XMLHttpRequest.sendAsBinary() ([polyfill available] (https://developer.mozilla.org/en-US/docs/DOM/XMLHttpRequest#sendAsBinary()). Malheureusement, cela signifie que l'utilisation de jQuery pour l'appel ajax devient beaucoup plus difficile. –