viernes, 15 de mayo de 2015

Ajax: Uploader un Archivo con AJAX sin Jquery

Almacenar un archivo a través de una pagina Web utilizando los formularios de HTML es bastante común, sin embargo realizar esta operación utilizando AJAX sin JQuery fue un poco más difícil encontrar la información de como se realizaba esta operación.

Lo primero que se debe realizar es crear un formulario con HTML que contenga solo el campo de tipo file que se utilizará para enviar el archivo, si en este formulario se agregan más campos estos pasarán junto con el archivo y luego habrá que limpiar el archivo para poder almacenarlo como corresponde. Si necesitas almacenar recibir más campos en el formulario puedes crear dos formularios, primero pasas el archivo a través de la función Ajax y una vez que eso termino correctamente puedes pasar el resto de los campos a través de otra función Ajax.

A continuación se presenta el código de un formulario HTML que permite recuperar el archivo desde una pagina web.

<form id="frm_subir_archivo" name="frm_subir_archivo" method="POST" enctype="multipart/form-data" accept-charset="utf-8">
<input type='file' id='fle_archivo>' name='fle_archivo' />
</form>
<br />
<input id='btn_guardar' type='button' value='Guardar' onClick='cargarArchivo();' />


Al observar se puede ver que el botón Guardar no está dentro del formulario, porque como se mencionó anteriormente, si estuviera incluido en el formulario el archivo contendría algunas lineas que representarían el valor que tiene el botón dentro del formulario.

Una vez que existe el formulario se debe crear la función encargada de recuperar el archivo y enviarlo al Servidor que lo tiene que almacenar. En este caso la función se llama cargarArchivo() y es llamada una vez que se presiona el botón Guardar.

A continuación se presenta el código de la función JavaScript que utiliza el ajax que envía el archivo al Servidor.

function cargarArchivo(){
    var archivo = $('#fle_archivo').val().trim();

    if(archivo == ""){
        alert('Debe seleccionar un archivo');
        return false;
    }

    //obtenemos un array con los datos del archivo
    archivo = $("#fle_archivo")[0].files[0];
    //obtenemos el nombre del archivo
    var archivo_nombre = archivo.name;
    //obtenemos la extensión del archivo
    var archivo_extension = archivo_nombre.substring(archivo_nombre.lastIndexOf('.') + 1);
    //obtenemos el tamaño del archivo
    var archivo_tamano = archivo.size;
    //obtenemos el tipo de archivo image/png ejemplo
    var archivo_tipo = archivo.type;

    var objeto = objetoAjax();
    //objeto.open("POST", "ajax.php", true);
    objeto.open("PUT", "ajax.php", true);
    objeto.onreadystatechange = function() {
        if (objeto.readyState === 0) {
            div_msg.html('<img src'+'='+'"./ajax-loader.gif"/> Preparando datos...');
        } else if (objeto.readyState === 1) {
            div_msg.html('<img src'+'='+'"./ajax-loader.gif"/> Procesando consulta...');
        } else if (objeto.readyState === 2) {
            div_msg.html('<img src'+'='+'"./ajax-loader.gif"/> Esperando respuesta...');
        } else if (objeto.readyState === 3) {
            div_msg.html('<img src'+'='+'"./ajax-loader.gif"/> Recibiendo respuesta...');
        } else if (objeto.readyState === 4) {
            var response = objeto.responseText.tratarResponseText();
            var estado = response.substring(0, 2);
            response = response.substring(2);
            if(estado == 'OK'){
                alert(response);
            }else if(estado == 'FL'){
                alert(response);
            }else if(estado == 'WN'){
                alert(response);
            }else if(estado == 'AL'){
                alert(response);
            }else{
            }
        }
    };
    //objeto.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
    objeto.setRequestHeader("Content-Type", "multipart/form-data");
    objeto.setRequestHeader("Content-length", file_size);
    objeto.setRequestHeader("X_FILENAME", file_name);
    var variables = archivo;
    objeto.send(variables);
}


Es necesario aclarar que el código utiliza Jquery para obtener el valor de los campos, pero no lo utiliza para realizar el envío de datos a través de Ajax.

Lo primero es verificar que el campo que contiene el archivo no venga vació, si está vacío se envía una alerta explicando el error y se de tiene el script. Si el campo es distinto de vacío se obtienen los diferentes valores que entrega un campo de tipo archivo, como son el nombre, la extensión, el tamaño y el tipo de archivo, sin olvidar la variable archivo que contiene el cuerpo del archivo.

Después se crea el objetoAjax, se abre la conexión con el servidor y se indica de que modo se enviarán los datos. Los métodos típicos para enviar datos desde un formulario HTML a un servidor son GET y POST, pero para este caso se utiliza PUT que se utiliza para almacenar archivos en un servidor.

Después se obtienen los diferentes cambios de estado de la conexión Ajax hasta llegar al estado 4 en el momento que el Script Ajax retorna la respuesta de la solicitud. El string de respuesta lo dividimos en los dos primeros caracteres que indican la condición de retorno OK (operación exitosa), FL (operación fallo), WN (peligro en la operación), AL (alerta en la operación) y se envía una alerta con una descripción de la respuesta.

Se agrega un header indicando el tipo de formulario que se envía al servidor, el tamaño del archivo y el nombre del archivo, para ser recibidos en el servidor, por último se envían los argumentos hacia el servidor, en este caso el archivo adjunto.

En la función cargarArchivo() se utiliza la función objetoAjax para crear un objeto que sea capaz de enviar la información obtenida al Servidor y la función tratarResponseText que permite recuperar la respuesta entregada por el servidor a la petición realizada desde el JavaScript. Ambas funciones se presentan en el siguiente código fuente.

// JavaScript Document  Objeto Ajax
String.prototype.tratarResponseText=function(){
    var pat=/<script[^>]*>([\S\s]*?)<\/script[^>]*>/ig;
    var pat2=/\b\s+src=[^>\s]+\b/g;
    var elementos = this.match(pat) || [];
    for(i=0;i<elementos.length;i++) {
        var nuevoScript = document.createElement('script');
        nuevoScript.type = 'text/javascript';
        var tienesrc=elementos[i].match(pat2) || [];
        if(tienesrc.length){
            nuevoScript.src=tienesrc[0].split("'").join('').split('"').join('').split('src=').join('').split(' ').join('');
        }else{
            var elemento = elementos[i].replace(pat,'$1');
            nuevoScript.text = elemento;
        }
        document.getElementsByTagName('body')[0].appendChild(nuevoScript);
    }
    return this.replace(pat,'');
}

function objetoAjax(){
    var xmlhttp=false;
    try {
        xmlhttp = new ActiveXObject("Msxml2.XMLHTTP");
    } catch (e) {
        try {
           xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
        } catch (E) {
            xmlhttp = false;
        }
    }

    if (!xmlhttp && typeof XMLHttpRequest!='undefined') {
        xmlhttp = new XMLHttpRequest();
    }
    return xmlhttp;
}


Ahora que se ha explicado todos los códigos desde el lado del cliente, se puede ver como se recupera el archivo en el servidor. El código para recuperar el archivo y almacenar el archivo se presenta a continuación.

if (isset($_SERVER['HTTP_X_FILENAME'])) {
    $archivo_nombre = $_SERVER['HTTP_X_FILENAME'];
    $archivo_nombre = date('YmdHis')."_".$archivo_nombre;
    file_put_contents(
        '/tmp/'.$archivo_nombre,
        file_get_contents('php://input')
    );
    echo 'OK'.$archivo_nombre;
}else{
    echo 'FLEl archivo no fue almacenado.';
}


Se pregunta si existe el nombre del archivo, de no ser así se devuelve un error indicando que el archivo no fue almacenado. Si existe el nombre de archivo se recupera en una variable a la cual se agrega la fecha para que no se borren los archivos subidos con el mismo nombre, después se recupera el contenido del archivo y se pasa a una string con la función file_get_contents(). Con la función file_put_contents que escribe el contenido de un string a un archivo ubicado en la dirección indicada como primer parámetro.

Con estos códigos se puede recuperar un archivo desde la interfaz web del cliente y enviar a un servidor a través de Ajax, sin utilizar Jquery para enviar este archivo.

Referencia:

http://uno-de-piera.com/subir-imagenes-con-php-y-jquery/
http://www.eduardocasas.com/es/blog/10-11-2012/xmlhttprequest-2-subiendo-archivos-al-servidor-por-medio-de-ajax
http://aprende-web.net/progra/ajax/ajax_11.php
https://github.com/FineUploader/fine-uploader/issues/61

"Gracias, por compartir tus conocimientos"