2010-10-23 32 views
77

Certo, ci sono domande simili in giro su Stack Overflow, ma sembra che nessuno soddisfi le mie esigenze.Utilizzo di caricamenti di file HTML5 con AJAX e jQuery

Ecco quello che sto cercando di fare:

  • Carica un'intera forma di dati, un pezzo di che è un singolo file di
  • Lavora con biblioteca upload di file di Codeigniter

Fino a qui, tutto va bene. I dati vengono nel mio database in quanto ne ho bisogno. Ma mi piacerebbe anche presentare il mio modulo tramite un posto AJAX:

  • Utilizzando l'API native HTML5 file, senza flash o una soluzione iframe
  • Preferibilmente l'interfacciamento con il metodo di basso livello .ajax() jQuery

Penso che potrei immaginare come fare questo caricando automaticamente il file quando il valore del campo cambia usando puro javascript, ma preferirei fare tutto in un colpo solo per inviare in jQuery. Sto pensando che non è possibile fare tramite stringhe di query in quanto ho bisogno di passare l'intero oggetto file, ma sono un po 'perso su cosa fare a questo punto.

Questo obiettivo può essere raggiunto?

+0

Non ho idea della parte Codeigniter, ma per la parte jQuery, dai un'occhiata a [questo plugin] (http://code.google.com/p/jquery-html5-upload/). – BalusC

+3

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

risposta

85

Non è troppo difficile. Per prima cosa, dai un'occhiata a FileReader Interface.

Così, quando il modulo viene inviato, prendere il processo di presentazione e

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

Poi, sul lato server (cioè 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); 

o qualcosa di simile. Potrei sbagliarmi (e se lo sono, per favore, correggimi), ma questo dovrebbe memorizzare il file come qualcosa come 1287916771myPicture.jpg in /uploads/ sul tuo server, e rispondere con una variabile JSON (a una funzione continueSubmission()) contenente il fileName sul server .

Check out fwrite() e jQuery.post().

Nella pagina sopra è descritto come utilizzare readAsBinaryString(), readAsDataUrl() e readAsArrayBuffer() per le altre esigenze (ad esempio immagini, video, ecc.).

+0

Hey Clark, sto comprendendo correttamente?Questo invierà il file caricato non appena viene caricato nel costruttore FileReader dal file system, ignorando il gestore di .jax a basso livello di jQuery. Quindi il resto del modulo verrà inviato normalmente? –

+0

Va bene, quindi ho sbagliato a capire prima. Ora sto prendendo il readAsDataUrl di un'immagine, aggiungendolo al mio datastring in .ajax e inviando tutte le mie informazioni insieme. La mia soluzione precedente prevedeva la classe di input dei file di default di CodeIgniter che acquisiva i dati da $ _FILES ['field'], quindi sembra che avrò bisogno di passare a una soluzione diversa per analizzare i dati dell'immagine base64. Qualunque consiglio su questo è ben accetto, inviando la tua risposta qui sopra, e una volta finita l'implementazione, la contrassegnerò come corretta. –

+1

@Joshua Cody - Ho aggiornato la risposta per dare qualche dettaglio in più. Dovrai perdonare che non ho usato CodeIgniter in molte lune e non posso dirti come integrare questo codice nella loro base di codice. Non sono sicuro del motivo per cui è necessario caricare il file prima dell'invio, ma questo dovrebbe almeno darti un indizio. (Puoi anche inserire l'immagine in un database se è meglio per te). – clarkf

6

con jQuery (e senza formdata API) si può usare qualcosa di simile:

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!"); }); 
}); 

Con formdata API è sufficiente aggiungere tutti i campi del form per oggetto formdata e inviarlo via $ .ajax ({url: data: formData, processData: false, contentType: false, type: "POST"})

+0

Questa soluzione non affronta la limitazione che XMLHttpRequest.send() impone ai dati canalizzati attraverso di essa. Quando viene passata una stringa (come la multipart), send() non supporta i dati binari. Il tuo multipart qui sarà trattato come una stringa utf-8, e soffocherà o corromperà dati binari che non sono utf-8 validi. Se è davvero necessario evitare FormData, è necessario utilizzare XMLHttpRequest.sendAsBinary() ([polyfill available] (https://developer.mozilla.org/en-US/docs/DOM/XMLHttpRequest#sendAsBinary()). Sfortunatamente questo significa che usare jQuery per la chiamata ajax diventa molto più difficile. –

Problemi correlati