XMLHttpRequest 2: subiendo archivos al servidor por medio de AJAX

Tal vez uno de los grandes olvidados cuando se mencionan las novedades incorporadas en HTML5 (entiéndase HTML5, CSS3 y JavaScript) ha sido el avance del objeto XMLHttpRequest, base sobre la que descansa AJAX. Como ya sabemos, este objeto ha sido fundamental en el desarrollo de lo que se ha conocido como la web 2.0 y de la World Wide Web en general.

En febrero del 2008 la W3C publicó el primer borrador de la nueva versión de este objeto, "XMLHttpRequest Level 2". Entre las mejoras que presenta se encuentran la gestión del progreso de eventos, peticiones entre distintos servidores y el manejo de flujos de bytes. Nos centraremos en esta última característica ya que es la que nos permitirá subir archivos al servidor. ¿Se acabaron los trucos con iframes? No del todo puesto que la especificación aún esta en fase borrador y los navegadores de Microsoft (< 10) no la soportan. En diciembre del 2011 las especificaciones de ambos niveles se fusionaron por lo que, siendo estrictos, ya no existe el XMLHttpRequest Nivel 2 como tal.

Para estudiar su implementación más básica (sin barras de progreso y demás florituras que dejaré para otro artículo) he desarrollado a modo de ejemplo una pequeña aplicación con jQuery y PHP cuyo repositorio he colocado en GitHub. La aplicación tiene una estructura básica: un fichero index.html que corresponde a la vista la cual contiene el formulario de entrada, el controlador ajax_controller.php que gestiona la petición AJAX, la carpeta js que contiene la librería jQuery y el archivo file.js desde donde se llama al objeto XMLHttpRequest y la carpeta "uploads" donde se guardarán los archivos subidos.

Empezaremos por la vista, donde llamamos a la librería jQuery y al fichero file.js y pintamos el formulario, el cual consta de un único input:

n

<!-- index.html -->

<!DOCTYPE html>
<html lang="es">
    <head>
        <meta charset="UTF-8">
        <title>XMLHttpRequest2 File Upload Example App</title>
        <script src="js/jquery.js"></script>
        <script src="js/file.js"></script>
    </head>
    <body>
        <form action="#" method="post">
            <input type="file" id="input_file">
        </form>
    </body>
</html>

A continuación el código del archivo file.js, donde realizamos la petición vía post al servidor cuando seleccionamos un fichero, el cual activa la función change de jQuery. Se debe hacer la petición X_FILENAME la cual será procesada por nuestro backend (fichero ajax_controller.php).

// js/file.js

$(document).ready
(
    function ()
    {
        var xhr = new XMLHttpRequest();
        $("#input_file").change
        (
            function(event)
            {
                var file = event.target.files[0];
                xhr.open('POST', 'ajax_controller.php', true);
                xhr.setRequestHeader("X_FILENAME", file.name);
                xhr.send(file);
            }
        );
    }
);

Está estructura es compatible con todos los navegadores que soportan la última especificación del objeto, incluido Internet Explorer 10 (sí, como lo oyes). El fichero equivalente para los que opten por una solución con el objeto $.ajax de jQuery sería el siguiente:

$(document).ready
(
   
 function ()
    {
        $("#input_file").change
        (
            function(event)
            {
                var file = event.target.files[0];
                $.ajax
                (
                    {
                        beforeSend: function(xhr)
                        {
                            xhr.setRequestHeader("X_FILENAME", file.name);
                        },
                        type: 'POST',
                        url: 'ajax_controller.php',
                        processData: false,
                        data: file
                    }
                );
            }
        );
    }
);

Por último el código correspondiente al fichero ajax_controller.php. En este ejemplo se hace uso de las funciones file_get_contents y file_put_contents para leer y colocar el fichero en la carpeta "uploads", respectivamente:

// ajax_controller.php

<?php
if (isset($_SERVER['HTTP_X_FILENAME'])) {
    file_put_contents(
        'uploads/'.$_SERVER['HTTP_X_FILENAME'],
        file_get_contents('php://input')
    );
}

Pues nada más, aquí termina este mini tutorial. Más adelante añadiré mejoras a la aplicación como una barra de progreso y un "drag and drop", pero todo esto lo dejaré para otro artículo.

Comments

You must sing in to post a comment.

There are no comments for this article.