2010-10-23 19 views
77

Es cierto que hay preguntas similares en Stack Overflow, pero parece que ninguna cumple con mis requisitos.Uso de cargas de archivos HTML5 con AJAX y jQuery

Aquí es lo que estoy tratando de hacer:

  • Subir un formulario completo de datos, de una sola pieza de los cuales es un sola archivo
  • trabajo con la biblioteca de carga de archivos de CodeIgniter

Hasta aquí, todo está bien. Los datos entran en mi base de datos cuando lo necesito. Pero también me gustaría presentar mi formulario a través de un puesto de AJAX:

  • Uso de la API HTML5 de archivo nativo, no flash o una solución de marco flotante
  • Preferiblemente interfaz con el método de bajo nivel .ajax() jQuery

Creo que podría imaginar cómo hacerlo cargando automáticamente el archivo cuando el valor del campo cambie usando javascript puro, pero prefiero hacerlo todo de una sola vez para enviarlo en jQuery. Estoy pensando que no es posible hacerlo a través de cadenas de consulta ya que necesito pasar todo el objeto de archivo, pero estoy un poco perdido sobre qué hacer en este momento.

¿Se puede lograr esto?

+0

No tengo idea de la parte de Codeigniter, pero para la parte jQuery, busca [este complemento] (http://code.google.com/p/jquery-html5-upload/). – BalusC

+3

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

Respuesta

85

No es demasiado difícil. En primer lugar, eche un vistazo a FileReader Interface.

lo tanto, cuando se envía el formulario, coger el proceso de presentación y

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

A continuación, en el lado del servidor (es decir 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 algo por el estilo. Puedo estar equivocado (y si lo estoy, por favor, corregirme), pero esto debería almacenar el archivo como 1287916771myPicture.jpg en /uploads/ en su servidor, y responder con una variable JSON (a una función continueSubmission()) que contiene el nombre de archivo en el servidor .

Consulte fwrite() y jQuery.post().

En la página anterior se detalla cómo usar readAsBinaryString(), readAsDataUrl() y readAsArrayBuffer() para sus otras necesidades (por ejemplo, imágenes, videos, etc.).

+0

Hola Clark, ¿entiendo bien?Esto enviará el archivo cargado tan pronto como se cargue en el constructor de FileReader desde el sistema de archivos, evitando el manejador .ajax de bajo nivel de jQuery. Entonces, ¿el resto del formulario se enviará normalmente? –

+0

Está bien, entonces estaba equivocado en mi comprensión antes. Ahora tomo ReadAsDataUrl de una imagen, la agrego a mi base de datos en .ajax y envío toda mi información. Mi solución anterior involucraba la clase de entrada de archivo predeterminada de CodeIgniter que tomaba datos de $ _FILES ['field'], por lo que parece que tendré que cambiar a una solución diferente para analizar los datos de imagen de base64. Cualquier consejo sobre eso es bienvenido, votando su respuesta aquí, y una vez que termine la implementación, lo marcaré como correcto. –

+1

@Joshua Cody - Actualicé la respuesta para dar un poco más de detalle. Tendrás que perdonar que no he usado CodeIgniter en muchas lunas y no podría decirte cómo integrar esto en su base de código. No estoy seguro de por qué necesita subir el archivo antes de enviarlo, pero al menos esto debería darle una pista. (También puede insertar la imagen en una base de datos si eso es mejor para usted). – clarkf

6

con jQuery (y sin FormData API) se puede usar algo como esto:

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 API FormData sólo hay que añadir todos los campos de su formulario para FormData objeto y enviarlo por $ .ajax ({url: url, data: formData, processData: false, contentType: false, escribe: "POST"})

+0

Esta solución no aborda la limitación que XMLHttpRequest.send() impone sobre los datos canalizados a través de ella. Cuando pasa una cadena (como su multiparte), send() no admite datos binarios. Su multiparte aquí se tratará como una cadena utf-8, y atascará o dañará datos binarios que no son válidos para utf-8. Si realmente necesita evitar FormData, necesita usar XMLHttpRequest.sendAsBinary() ([polyfill disponible] (https://developer.mozilla.org/en-US/docs/DOM/XMLHttpRequest#sendAsBinary()). Desafortunadamente, esto significa que usar jQuery para la llamada ajax se vuelve mucho más difícil. –

Cuestiones relacionadas