Sección de Artículos

¿Cómo utilizar el plugin fileinput-bootstrap para la subida masiva de archivos?


¿Cómo utilizar el plugin fileinput-bootstrap para la subida masiva de archivos?


avatar
(Programación)
Escrito el 06-07-2017 a las 15:58

Normalmente cuando necesitamos enviar más de un fichero a nuestro servidor por medio de un formulario, tenemos que incluir tantos campos de tipo file como ficheros deseemos subir. Este método de trabajo resulta engorroso de manipular y al mismo tiempo nos limita el total de ficheros que deseamos subir en cada envío.

Fileinput-Bootstrap

Una alternativa más práctica y sencilla que los métodos tradicionales para realizar el envío de múltiples ficheros (uploads) en nuestras aplicaciones web, es utilizar algún plugin o librería que nos facilite esta tarea.

Fileinput es una librería creada por Kartik Visweswaran y desarrollada en javascript que podemos utilizar en nuestros proyectos, que nos permite convertir un simple campo de tipo file en un control avanzado para la subida de archivos, incorporando compatibilidades para aquellos navegadores que no soporten el envío masivo de archivos.

Fileinput utiliza HTML5 junto con las características del framework Bootstrap 3.x para realizar la previsualización de archivos, permite la selección de múltiples ficheros, y mucho más.

Este plugin permite de una forma sencilla realizar una configuración avanzada de los controles de selección de archivos. Trabaja con estilos CSS3 Bootstrap y funciones de JQuery. Se mejora la funcionalidad de entrada de archivo, ofreciendo apoyo para realizar una vista previa antes de enviarlos al servidor de una amplia variedad de archivos: imágenes, texto, html, vídeo, ect... Además, incluye uploads basados en AJAX, permite arrastrar y soltar archivos, visualización del progreso de carga, añadir o borrar archivos.

Entre las principales características de este plug-in podemos destacar:

  • Selección de múltiples archivos.
  • Limitar el total de archivos permitidos en la selección.
  • Permite utilizar el método de arrastrar y soltar para la selección de archivos.
  • Filtrar los tipos de archivos permitidos para realizar el upload.
  • Vista previa para algunos tipos de archivos.
  • Eliminar archivos de la selección.
  • Subida independiente de archivos o de todos al mismo tiempo.
  • Mostrar el progreso del proceso de upload de archivos.
  • Configurar el idioma del control y los mensajes.
  • Utilizar eventos para el control del proceso.

Descargar el plugin

En la siguiente dirección tenemos disponible la descarga del plugin para incorporarlo a nuestra web: https://github.com/kartik-v/bootstrap-fileinput. En ella encontraremos a parte de los archivos fuente, indicaciones acerca de su funcionamiento e implementación en nuestra web.

Si deseamos una información mucho más amplia, así como ver multitud de ejemplos os recomiendo visitar la página del autor: http://plugins.krajee.com/file-input

Contenidos del plugin

Cuando nos descargamos bootstrap-fileinput, obtenemos toda la estructura de carpetas que componen el plugin con sus librerías, ejemplos y temas disponibles. De toda la información disponible, los mínimos componentes que necesitaremos para utilizarlo serán las carpetas:

  • css que contiene los archivos de estilo que utiliza el plugin, de entre los cuales sólo necesitaremos fileinput.min.css.
  • img que contiene dos gifs animados que muestra el plugin durante el proceso.
  • js que contiene el plugin fileinput.min.js, y los archivos para realizar las traducciones para cada idioma.

Como bootstrap-fileinput utiliza las características de Bootstrap para su correcto funcionamiento, a parte de los propios archivos del plugin que nos descargamos, necesitaremos incluir en la sección de cabecera de nuestra página los enlaces a los archivos de Bootstrap y JQuery.

Funcionamiento

En cuanto a los ficheros necesarios para todo el proceso, no hay diferencia con respecto al método tradicional: una página html con el formulario para el envío de los ficheros y un script PHP encargado de la recepción y proceso de los ficheros.

En la página html necesitaremos definir un formulario con un campo de tipo file que asignaremos posteriormente al plugin. El campo tiene la característica de que el nombre que le asignamos está en formato array (name=”NombreCampo[]”). Esta característica es la que nos va a permitir el envío de múltiples ficheros.

Después definiremos el script (javascript) donde inicializaremos el plugin con las opciones deseadas y definiremos los controladores de evento que deseemos utilizar del plugin.

El script en PHP será el encargado de recibir los ficheros, procesarlos y devolver una respuesta a la página del formulario indicando como ha ido el proceso.

Fileinput admite múltiples opciones de trabajo por medio de la utilización de propiedades predefinidas y también aporta eventos que nos permiten tener un control de lo que ocurre, cuando y como sucede todo el proceso (recomiendo que visitéis la web y para revisar la información disponible).

Fileinput transfiere los ficheros por medio de llamadas AJAX a un script del servidor que será el encargado de recibir y procesar cada envío. El script en PHP moverá los archivos recibidos a la carpeta del servidor destinada para almacenar los archivos y devolverá una respuesta en formato JSON que indique el estado del proceso realizado.

Inicializar el plugin

La instrucción para inicializar el plugin se expresa en JQuery utilizando la siguiente sintáxis:

$(‘#Selector-Input’).fileinput({Opción: Valor, Opción: Valor, ...});

Siendo Selector-Input el id del campo de tipo file del formulario y Lista de opciones las opciones de configuración de fileinput separadas por coma (,).

Opciones:

A la hora de inicializar bootstrap-fileinput podemos configurar su comportamiento utilizando sus opciones. El plugin suministrar multitud de opciones para el control de las vistas previas, archivos, mensajes, botones, etc..

Yo os voy a comentar un pequeño grupo de opciones que resultan de gran utilidad para la mayoría de entornos en los que tiene que trabajar.

  • languaje: dato de tipo string que indica el idioma en el que se tienen que mostrar los mensajes y textos del plugin. Los valores permitidos se corresponden con los archivos (.js) de traducciones que encontramos dentro del directorio js/locales/.

languaje: ‘es’ -> mensajes en español
languaje: ‘fr’ -> mensajes en francés
languaje: ‘de’ -> mensajes en alemán

  • allowedFileExtensions: array de tipo string donde indicamos las extensiones de los archivos que se pueden seleccionar.

allowedFileExtensions: [‘docx’,’pdf’,’png’] -> Indicamos que sólo se permiten documentos de Word y Adobe e imágenes png

  • maxFileSize: Tamaño máximo en Kb de los archivos que podemos seleccionar.

maxFileSize: 50 -> Limitamos a 50Kb el tamaño máximo de los archivos permitidos

  • maxFileCount: Número máximo de archivos que podemos seleccionar.

maxFileCount: 5 -> Limitamos a 5 el número máximo de archivos que se pueden seleccionar

  • uploadUrl: dato de tipo string, que indica la ruta de acceso al script PHP donde se envían los ficheros. Quizas en una de las más importantes, ya que si no la indicamos no podremos subir y almacenar los ficheros en el servidor.

uploadUrl: ‘recibe-ficheros.php’ -> Nombre del script encargado de recibir y procesar los archivos que se han seleccionado.

  • uploadAsync: dato de tipo booleano que activa o desactiva la subida asíncrona de archivos. Si lo establecemos a false (recomendable para peticiones AJAX) se realizará una sola llamada al servidor enviando toda la información en un array. Si lo establecemos a true se realizará una llamada al servidor por cada fichero enviado.

uploadAsync: false -> Desactivamos el envío asíncrono (individual) de ficheros.

  • removeFromPreviewOnError: dato de tipo booleano que activa o desactiva los mensajes de error a la hora de seleccionar archivos.

removeFromPreviewOnErrror: false -> Desactivamos los mensajes de error en la ventana de previsualización.

Ejemplo

Aunque las posibilidades de trabajo son muy amplias, nosotros sólo nos vamos a centrar en definir las propiedades mínimas para indicar el script que recibirá los datos, el idioma con el que deseamos que se muestren los mensajes y los tipos de archivos admitidos a la hora de realizar la selección.

Vamos a crear un formulario para el envío de documentos de Office y documentos pdf hasta un máximo de 5 archivos que llamará al script PHP desde donde los iremos guardando en la carpeta de descargas de nuestro servidor.

Previamente hemos descargado las carpetas js, img y css de fileinput y las hemos guardado en una carpeta de nombre fileinput de nuestro servidor.

Formulario con el plugin fileinput (form-fileinput.html).

En la sección de cabecera añadimos los enlaces necesarios a las librerías de fileinput y las librerías de Bootstrap y JQuery.

Crearemos un formulario Bootstrap con el campo de tipo file asignándole un nombre de tipo array para admitir múltiples ficheros.

Al final de la página crearemos el script javascript donde inicializaremos el plugin indicando el idioma, script de proceso, máximo número de archivos que se pueden seleccionar y tipos de archivos admitidos. También utilizaremos algunos eventos de fileinput para mostrar un resumen final del proceso.

<!DOCTYPE html>
<html lang="es">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">     
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
    <link href="./fileinput/css/fileinput.min.css" media="all" rel="stylesheet" type="text/css" />
    <script src="http://code.jquery.com/jquery-1.11.3.min.js"></script>
    <script src="./fileinput/js/fileinput.min.js" type="text/javascript"></script>
    <script src="./fileinput/js/locales/es.js" type="text/javascript"></script>	
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
    <title>Envío de multiples ficheros</title>
</head>
<body lang="es">
<div class="container">
    <p>&nbsp;</p>
    <p>&nbsp;</p>    
    <DIV id="PANEL_0" class="panel panel-primary text-justify">
        <DIV class="panel-heading">
            <H3 class="panel-title">Envio de solicitud</H3>
        </DIV>
        <DIV class="panel-body">
            <FORM enctype="multipart/form-data">
                <label for="file-es" role="button">Seleccionar Archivos</label>
                <input id="file-es" name="file-es[]" type="file" multiple>
                <SMALL class="form-text text-muted">Seleccionar archivos de Office 201X: docx, xlsx, pptx y pdf hasta un máximo de 5.</SMALL>
            </form>
            <p>&nbsp;</p>
            <div class="alert alert-success" role="alert"></div>
        </DIV>
    </DIV>        
</div>    
<script>
// Tipos de archivos admitidos por su extensión
  var tipos = ['docx','xlsx','pptx','pdf'];
// Contadores de archivos subidos por tipo
  var contadores=[0,0,0,0];
// Reinicia los contadores de tipos subidos
  var reset_contadores = function() {
    for(var i=0; i<tipos.length;i++) {
       contadores[i]=0;
    }
  };
// Incrementa el contador de tipo según la extensión del archivo subido	
  var contadores_tipos = function(archivo) {
    for(var i=0; i<tipos.length;i++) {
      if(archivo.indexOf(tipos[i])!=-1) {
        contadores[i]+=1;
        break;	
      }
    }
  };
// Inicializamos el plugin fileinput:
//  traducción al español
//  script para procesar las peticiones de subida
//  desactivar la subida asíncrona
//  máximo de ficheros que se pueden seleccionar	
//  Tamaño máximo en Kb de los ficheros que se pueden seleccionar
//  no mostrar los errores de tipo de archivo (cuando el usuario selecciona un archivo no permitido)
//  tipos de archivos permitidos por su extensión (array definido al principio del script)
  $('#file-es').fileinput({
      language: 'es',
      uploadUrl: '/recibe-fileinput.php',
      uploadAsync: false,
      maxFileCount: 5,
      maxFileSize: 75,
      removeFromPreviewOnError: true,
      allowedFileExtensions : tipos
  });
// Evento filecleared del plugin que se ejecuta cuando pulsamos el botón 'Quitar'
//    Vaciamos y ocultamos el div de alerta
  $('#file-es').on('filecleared', function(event) {
    $('div.alert').empty();
    $('div.alert').hide();		
  });
// Evento filebatchuploadsuccess del plugin que se ejecuta cuando se han enviado todos los archivos al servidor
//    Mostramos un resumen del proceso realizado
//    Carpeta donde se han almacenado y total de archivos movidos
//    Nombre y tamaño de cada archivo procesado
//    Totales de archivos por tipo
  $('#file-es').on('filebatchuploadsuccess', function(event, data, previewId, index) {
    var ficheros = data.files;
    var respuesta = data.response;
    var total = data.filescount;
    var mensaje;
    var archivo;
    var total_tipos='';
	
    reset_contadores(); // Resetamos los contadores de tipo de archivo
    // Comenzamos a crear el mensaje que se mostrará en el DIV de alerta
    mensaje='<p>'+total+ ' ficheros almacenados en la carpeta: '+respuesta.dirupload+'<br><br>';
    mensaje+='Ficheros procesados:</p><ul>';
    // Procesamos la lista de ficheros para crear las líneas con sus nombres y tamaños
    for(var i=0;i<ficheros.length;i++) {
      if(ficheros[i]!=undefined) {
        archivo=ficheros[i];				
        tam=archivo.size / 1024;
        mensaje+='<li>'+archivo.name+' ('+Math.ceil(tam)+'Kb)'+'</li>';
        contadores_tipos(archivo.name);  // Incrementamos el contador para el tipo de archivo subido
      } 
    };
		
    mensaje+='</ul><br/>';
    // Línea que muestra el total de ficheros por tipo que se han subido
    for(var i=0; i<contadores.length; i++)  total_tipos+='('+contadores[i]+') '+tipos[i]+', ';
    // Apaño para eliminar la coma y el espacio (, ) que se queda en el último procesado
    total_tipos=total_tipos.substr(0,total_tipos.length-2);
    mensaje+='<p>'+total_tipos+'</p>';
    // Si el total de archivos indicados por el plugin coincide con el total que hemos recibido en la respuesta del script PHP
    // mostramos mensaje de proceso correcto
    if(respuesta.total==total) mensaje+='<p>Coinciden con el total de archivos procesados en el servidor.</p>';
    else mensaje+='<p>No coinciden los archivos enviados con el total de archivos procesados en el servidor.</p>';
    // Una vez creado todo el mensaje lo cargamos en el DIV de alerta y lo mostramos
    $('div.alert').html(mensaje);
    $('div.alert').show();
  });
// Ocultamos el div de alerta donde se muestra un resumen del proceso
  $('div.alert').hide();
	
</script>
</body>
</html>

 

Script encargado de procesar los archivos recibidos (recibe-fileinput.php)

 

<?php
// COMPROBACIÓN INICIAL ANTES DE CONTINUAR CON EL PROCESO DE UPLOAD
// **********************************************************************

// Si no se ha llegado ha definir el array global $_FILES, cancelaremos el resto del proceso
if (empty($_FILES['file-es'])) {
	// Devolvemos un array asociativo con la clave error en formato JSON como respuesta	
    echo json_encode(['error'=>'No hay ficheros para realizar upload.']); 
	// Cancelamos el resto del script
    return; 
}

// DEFINICIÓN DE LAS VARIABLES DE TRABAJO (CONSTANTES, ARRAYS Y VARIABLES)
// ************************************************************************

// Definimos la constante con el directorio de destino de las descargas
define('DIR_DESCARGAS',__DIR__.DIRECTORY_SEPARATOR .'descargas');
// Obtenemos el array de ficheros enviados
$ficheros = $_FILES['file-es'];
// Establecemos el indicador de proceso correcto (simplemente no indicando nada)
$estado_proceso = NULL;
// Paths para almacenar
$paths= array();
// Obtenemos los nombres de los ficheros
$nombres_ficheros = $ficheros['name'];

// LÍNEAS ENCARGADAS DE REALIZAR EL PROCESO DE UPLOAD POR CADA FICHERO RECIBIDO
// ****************************************************************************

// Si no existe la carpeta de destino la creamos
if(!file_exists(DIR_DESCARGAS)) @mkdir(DIR_DESCARGAS);
// Sólo en el caso de que exista esta carpeta realizaremos el proceso
if(file_exists(DIR_DESCARGAS)) {
	// Recorremos el array de nombres para realizar proceso de upload
	for($i=0; $i < count($nombres_ficheros); $i++){
		// Extraemos el nombre y la extensión del nombre completo del fichero
		$nombre_extension = explode('.', basename($nombres_ficheros[$i]));
		// Obtenemos la extensión
		$extension=array_pop($nombre_extension);
		// Obtenemos el nombre
		$nombre=array_pop($nombre_extension);
		// Creamos la ruta de destino
		$archivo_destino = DIR_DESCARGAS . DIRECTORY_SEPARATOR . utf8_decode($nombre) . '.' . $extension;
		// Mover el archivo de la carpeta temporal a la nueva ubicación
		if(move_uploaded_file($ficheros['tmp_name'][$i], $archivo_destino)) {
			// Activamos el indicador de proceso correcto
			$estado_proceso = true;
			// Almacenamos el nombre del archivo de destino
			$paths[] = $archivo_destino;
		} else {
			// Activamos el indicador de proceso erroneo		
			$estado_proceso = false;
			// Rompemos el bucle para que no continue procesando ficheros
			break;
		}
	}
}
// PREPARAR LAS RESPUESTAS SOBRE EL ESTADO DEL PROCESO REALIZADO
// **********************************************************************

// Definimos un array donde almacenar las respuestas del estado del proceso
$respuestas = array();
// Comprobamos si el estado del proceso a finalizado de forma correcta
if ($estado_proceso === true) {
	/* Podríamos almacenar información adicional en una base de datos
	   con el resto de los datos enviados por el método POST */

	// Como mínimo tendremos que devolver una respuesta correcta por medio de un array vacio.
    $respuestas = array();
	$respuestas = ['dirupload' => basename(DIR_DESCARGAS), 'total'=>count($paths)]; 
	/* Podemos devolver cualquier otra información adicional que necesitemos por medio de un array asociativo
       Por ejemplo, prodríamos devolver la lista de ficheros subidos de esta manera: 
       	$respuestas = ['ficheros' => $paths]; 
	   Posteriormente desde el evento fileuploaded del plugin iríamos mostrando el array de ficheros utilizando la propiedad response
	   del parámetro data: 
	   	respuesta = data.response; 
		respuesta.ficheros.forEach(function(nombre) {alert(nombre); });
	*/
} elseif ($estado_proceso === false) {
    $respuestas = ['error'=>'Error al subir los archivos. Póngase en contacto con el administrador del sistema'];
    // Eliminamos todos los archivos subidos
    foreach ($paths as $fichero) {
        unlink($fichero);
    }
// Si no se han llegado a procesar ficheros $estado_proceso seguirá siendo NULL
} else {
    $respuestas = ['error'=>'No se ha procesado ficheros.'];
}

// RESPUESTA DEVUELTA POR EL SCRIPT EN FORMATO JSON
// **********************************************************************

// Devolvemos el array asociativo en formato JSON como respuesta
echo json_encode($respuestas);
?>

 

Ejemplo en acción:

Supongamos que seleccionamos 5 ficheros de tipo Word, Excel, Pdf y PowerPoint:

 

Formulario con 5 archivos seleccionados

 

Cuando pulsemos el botón de ‘Subir archivo’ el script PHP se encargará de moverlos a la carpeta de descargas del servidor y devolver una respuesta con el total de ficheros procesados y el nombre de la carpeta donde se han almacenado.

El script (javascript) de la página form-fileinput.html, se encargará de crear el resumen en formato HTML con los datos obtenidos del plugin y la respuesta del script PHP y mostrará el siguiente mensaje debajo del formulario:

 

Resumen de final del proceso

 

Scripts de ejemploDescargar scripts de ejemplo