> Manuales > Manual de Ajax práctico

Ejemplo de upload de archivos con Javascript (y código PHP del lado del servidor) que usa la técnica del iframe para navegadores antiguos donde no era posible el uso de Ajax para subir los ficheros de manera asíncrona.

Upload de archivos emulando Ajax con la técnica del iframe

En este artículo vamos a ver un ejemplo de upload de archivos usando Ajax, tal como se realizaba para navegadores antiguos, donde no existía la posibilidad de enviar datos por Ajax. En estos casos se usaba un truco que emulaba la experiencia de usuario de Ajax usando un <iframe>.

Esta técnica sigue funcionando actualmente en todos los navegadores, aunque a decir verdad no es totalmente necesaria ya, porque con FormData podemos obtener los datos del archivo y con la cabecera "Content-Type": "multipart/form-data" podemos indicar al servidor que vamos a enviar datos binarios. Un ejemplo de Ajax con FormData lo tienes en el artículo Enviar datos por POST por Ajax con los API Javascript Fetch y FormData.

Motivación de la técnica iframe para emuluar un upload por Ajax

Por una necesidad personal yo también me vi involucrado en esta serie de discusiones. La finalidad de este artículo es que le sea útil a alguien; principalmente a mi.

El título correcto de este artículo sería algo como Archivo Subido con AJAX. Esto, hoy por hoy (en la época en la que se redactó el artículo), no se puede hacer. Es así y no hay vuelta que darle.

Hay sobradas muestras que el objeto XMLHttpRequest no puede enviar archivos al servidor. Entonces deberíamos cambiar el titulo del post. Sería así:

Ajax File Upload con experiencia Ajax pero SIN Ajax

Frente a esta inmutable condición los desarrolladores han buscado una buena forma de simular scripts para subir archivos al servidor como si fuese con AJAX, y por lo menos conmigo lo lograron.

Primer Ejemplo - Subir un Archivo el Server

Utilizaremos tan solo dos scripts muy sencillos. Luego iremos mejorando y complementando el desarrollo hasta llegar a nuestro objetivo.

HTML:

<form method="post" enctype="multipart/form-data"
action="controlUpload.php"
target="iframeUpload">
<input type="hidden" name="phpMyAdmin" />
Archivo: <input name="fileUpload" type="file" />
<input type="submit" value="enviar">
<iframe name="iframeUpload"></iframe>
</form>

Es es un simple formulario HTML con un campo FILE y un marco flotante iframe denominado iframeUpload. Cuando enviamos el archivo al servidor ejecutaremos el script controlUpload.php y la respuesta del server se hará en nuestro iframe ya que apuntamos al mismo dentro de la etiqueta target en la declaración form.

PHP:

<?php
// Script Que copia el archivo temporal subido al servidor en un directorio.
echo '<p>Nombre Temporal: ' . $_FILES['fileUpload']['tmp_name'] . '</p>';
echo '<p>Nombre en el Server: ' . $_FILES['fileUpload']['name'] . '</p>';
echo '<p>Tipo de Archivo: ' . $_FILES['fileUpload']['type'];
$tipo = substr($_FILES['fileUpload']['type'], 0, 5);
// Definimos Directorio donde se guarda el archivo
$dir = 'archs/';
// Intentamos Subir Archivo
// (1) Comprovamos que existe el nombre temporal del archivo
if (isset($_FILES['fileUpload']['tmp_name'])) {
  // (2) - Comprovamos que se trata de un archivo de imágen
  if ($tipo == 'image') {
    // (3) Por ultimo se intenta copiar el archivo al servidor.
    if (!copy($_FILES['fileUpload']['tmp_name'], $dir . $_FILES['fileUpload']['name']))
      echo '
  <script> alert("Error al Subir el Archivo");</script>';
  } else
    echo 'El Archivo que se intenta subir NO ES del tipo Imagen.';
} else
  echo 'El Archivo no ha llegado al Servidor.';
?>

Cuando se ejecuta el script, este intenta rescatar los datos del archivo a subir. Para eso se vale del array global de PHP $_FILES. Si no estas muy familiarizado recomiendo que leas esta sección del manual oficial para entender su funcionamiento y sus particularidades.

En las primeras líneas presentamos el nombre del archivo temporal generado por nuestro motor PHP, el segundo es el nombre real del archivo y el tercero es el tipo de archivo.

Si estos valores están definidos evidentemente el archivo llego al server. Estas líneas mas adelante no las utilizaremos; ahora sólo las usamos para realizar el seguimiento del script.

Asignamos a $tipo el tipo de archivo para poder controlar que sea una imágen. Se cortan los primeros 5 caracteres y si todo es correcto $tipo tendrá el valor image. Si no es una imagen tendrá un valor distinto. Este control se puede hacer de diversas formas; nosotros utilizamos esta.

Luego, intentamos copiar el archivo temporal en forma definitiva en algún sector físico en nuestro servidor. Se verifica en forma anidada definición de nombre temporal de archivo, tipo de archivo y si el motor concretó la copia.

En este ejemplo lo hacemos en la carpeta archs/. Si está todo bien podemos pasar al segundo paso.

Aclaraciones: Hay que definir permisos de escritura de PHP en el directorio a copiar el archivo. En nuestro caso es la carpeta archs.

Transformando el ejemplo para emular Ajax mediantel el iframe

Empecemos a modificar un poco los archivos anteriores. Comenzamos observando cómo va a quedar nuestro formulario HTML.

<form 
  method="post" 
  enctype="multipart/form-data"
  action="controlUpload2.php" 
  target="iframeUpload"
>
    Archivo: <input name="fileUpload" type="file" onchange="javascript: submit()" />
    <br /><iframe name="iframeUpload" style="display:none"></iframe>
</form>

En primer lugar hemos eliminado el botón submit Enviar y vamos a disparar el formulario con una orden en JS. Eso lo conseguimos con el onchange del input:

onchange="javascript: submit()"

Luego se necesita ocultar el iframe para que esta parte no aparezca en la página, ya que es un elemento que usamos solamente de manera interna.

style="display:none"

Al final del archivo agregamos una línea que nos dirige a un simple script que nos muestra los archivos subidos al server; más que nada para que puedas corroborar la funcionalidad.

<a href="verArchivos.php">Ver Archivos</a>

Ahora, al script controlUpload.php le vamos a agregar alertas de control (también con JS por ahora). Además vamos a eliminar sentencias que se han vuelto innecesarias al ocular el iframe.

<?php
// Script Que copia el archivo temporal subido al servidor en un directorio.
$tipo = substr($_FILES['fileUpload']['type'], 0, 5);
// Definimos Directorio donde se guarda el archivo
$dir = 'archs/';
// Intentamos Subir Archivo
// (1) Comprovamos que existe el nombre temporal del archivo
if (isset($_FILES['fileUpload']['tmp_name'])) {
  // (2) - Comprovamos que se trata de un archivo de imágen
  if ($tipo == 'image') {
    // (3) Por ultimo se intenta copiar el archivo al servidor.
    if (!copy($_FILES['fileUpload']['tmp_name'], $dir . $_FILES['fileUpload']['name']))
      echo '<script> alert("Error al Subir el Archivo");</script>';
    else
      echo '<script> alert("El archivo ' . $_FILES['fileUpload']['name'] . ' se ha copiado con Exito");</script>';
  } else
    echo '<script> alert("El Archivo que se intenta subir NO ES del tipo Imagen.");</script>';
} else {
  echo '<script> alert("El Archivo no ha llegado al Servidor.");</script>';
}
?>

Ya se empieza a parecer a un verdadero AJAX FILE UPLOAD. Que siga el circo !. Show must go on.

Finalmente un sencillo Script Javascript

Con estas últimas modificaciones tendremos la base final para poder implementar nuestro FILE UPLOAD con un comportamiento muy similar a gmail.
Al formulario lo vamos a contener en un div con id formUpload.

HTML:

<div id="formUpload">

Al archivo upload3.php, aparte del formulario de envío le vamos a agregar una sencilla función resultadoUpload (estado, file) realizada en JS que, dependiendo del código que nos 'envíe' controlUpload.php en las variales estado y file (ahora vemos como ...) imprimirá en dicho div un mensaje de respuesta al intento de subida.

JavaScript:

function resultadoUpload(estado, file) {
  var link = '<br /><br /><a href="upload3.php">Subir Archivo</a> - <a href="verArchivos.php">Ver Imagenes</a>';
  if (estado == 0) {
    var mensaje = 'El Archivo <a href="archs/' + file + '" target="_blank">' + file + '</a> se ha subido al servidor correctamente' + link;
  }
  if (estado == 1) {
    var mensaje = 'Error ! - El Archivo no llego al servdor' + link;
  }
  if (estado == 2) {
    var mensaje = 'Error ! - Solo se permiten Archivos tipo Imagen' + link;
  }
  if (estado == 3) {
    var mensaje = 'Error ! - No se pudo copiar Archivo. Posible problema de permisos en server' + link;
  }
  document.getElementById('formUpload').innerHTML = mensaje;
}

La impresión de nuestro mensaje en el div lo hace en la última linea de la función.

document.getElementById('formUpload').innerHTML=mensaje;

Ahora solo resta modificar controlUpload.php para que en vez de ejecutar una ventana alert () (como en el ejemplo anterior) simplemente ejecute el código JavaScript para llamar a la función resultadoFile () enviándole los datos correspondientes al intento de subida del archivo.
El punto llamativo es como accedemos desde el iframe oculto a la función que se encuentra en la página que lo contiene. Utilizamos la palabra reservada de JS parent.

<script>parent.resultadoUpload(estado, file);</script>

Finalizando

Este humilde tutorial se hizo más largo de lo que hubiese querido. Voy a seguir trabajando para mejorar el funcionamiento y evolucionarlo. Queda pendiente una barra de progreso (ahí creo que no podemos escapar de AJAX) y otras formas de aplicación.

Tomen al mismo y a todo el código como una guia par desarrollar esta pequeña aplicación. Faltan muchos controles de errores, de seguridad, etc. El código fuente presentado no es idéntico a los archivos que se pueden descargar; esto es simplemente por una cuestión de prolijidad.

Disculpen mi desorden semántico y gramatical; como la mayoría, soy atrevido al jugar el papel de tutor. Nada más lejos de mis fines. Simplemente lo mío es ayudar como así también me han ayudado.

Damián Suárez

Impulsor de XiFOX.net

Manual