Lenguaje PHP

Drag and Drop subir archivos con bootstrap y php

DropzoneJS subir archivos usando Dropzone y PHP

Drag and Drop subir archivos con bootstrap y php. En este artículo vamos a realizar un ejemplo completo de cómo subir ficheros al servidor haciendo uso de varias librerías y herramientas.

Este ejemplo se podrá subir masivamente archivos al servidor. Sin embargo, es bueno encontrar el plugin ideal para incorporar la subida masiva de archivos y de esta forma optimizar el trabajo de subida.

Cómo subir archivos Drag and Drop mediante DropzoneJS

Solo necesitaremos dos archivos de la librería de Dropzone: dropzone.js y dropzone.css. Por lo tanto, si deseas descargarte estos dos ficheros de la página oficial de DropzoneJS y obtener el paquete actualizado.

  • Descargar la librería DropzoneJS y colocar dentro de una carpeta js e insertar el fichero dropzone.js
  • Una carpeta css para el fichero dropzone.css
  • Crear una carpeta «images» para almacenar los archivos subidos por PHP
  • Un fichero index.php para mostrar el formulario
  • Fichero upload.php para validar la subida del fichero al servidor
  • Images.php se encargará de visualizar los archivos subidos.

¿Qué es Dropzonejs?

Es una biblioteca de código abierto que proporciona la carga de archivos arrastrando y soltando (drag and drop). Sin embargo, una de las ventajas es que es muy liviano ya que no depende de ninguna otra librería (como jQuery), etc.

Ventajas de usar Dropzonejs

Dropzonejs quizá es uno de las librerías más interesantes y gratuitas que hay para implementar la múltiple subida de archivos del lado del cliente debido a que de cara al usuario es muy práctico y rápido.

La librería nos va a ahorrar mucho trabajo a la hora de subir archivos al servidor.

Descarga de la Biblioteca

Esta bibliotrca puede ser descargado desde su página oficial.

¿Qué herramientas necesito?

Lo detallaremos a continuación:

  • Formulario con la posibilidad de subir múltiples archivos al servidor
  • La biblioteca Dropzonejs.
  • Framework Bootstrap 4.5.
  • Lenguaje PHP
  • jQuery Sortable.

Declaración de librerías

En la cabecera de la página principal o dentro de la etiqueta <head> declaramos los estilos y scripts necesarios para hacer funcionar el ejemplo completo.

<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css">
<link rel="stylesheet" href="css/styles.css">
<script src="https://code.jquery.com/jquery-3.2.1.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js"></script>
<script src="js/dropzone.js"></script>
<script src="js/jquery-ui.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.2/css/all.min.css" />

DropzoneJS subir archivos: Estructura

A continuación, mostraremos los archivos completos del ejemplo

A) Formulario: index.php

Este fichero es la parte principal porque mostrara el formulario de subida de los ficheros al servidor

<!DOCTYPE html>
<html lang="es" class="h-100">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

<!-- Bootstrap CSS -->
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" integrity="sha384-JcKb8q3iqJ61gNV9KGb8thSsNjpSL0n8PARn9HuZOnIxN0hoP+VmmDGMN5t9UJ0Z" crossorigin="anonymous">
<link rel="stylesheet" href="css/styles.css">
<script src="https://code.jquery.com/jquery-3.2.1.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js"></script>
<script src="js/dropzone.js"></script>
<script src="js/jquery-ui.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.2/css/all.min.css" integrity="sha512-HK5fgLBL+xu6dm/Ii3z4xhlSUyZgTT9tuc/hSrtw6uzJOvgRr2a9jyxxT1ely+B+xFAmJKVSTbpM/CuL7qxO8w==" crossorigin="anonymous" />

<title>Dropzone múltiple subida de archivos</title>

</head>
<body class="d-flex flex-column h-100">

<header>
<!-- Fixed navbar -->
<nav class="navbar navbar-expand-md navbar-dark bg-dark">
<div class="container">
<a class="navbar-brand" href="index.php">BaulPHP</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarCollapse" aria-controls="navbarCollapse" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarCollapse">
<ul class="navbar-nav mr-auto">
<li class="nav-item active">
<a class="nav-link" href="images.php">Subidas <span class="sr-only">(current)</span></a>
</li>
</ul>
<form class="form-inline mt-2 mt-md-0">
<input class="form-control mr-sm-2" type="text" placeholder="Search" aria-label="Search">
<button class="btn btn-outline-success my-2 my-sm-0" type="submit">Busqueda</button>
</form>
</div>
</div>
</nav>
</header>

<!-- Begin page content -->
<hr>
<br>
<main>
<div class="container">

<div class="row">
<div class="col-md-12">
<h3>Dropzone múltiple subida de archivos</h3>
<hr>
</div>
</div>
<div class="row">
<div id="content" class="col-lg-12">
<form action="index.php" method="post" enctype="multipart/form-data">
<div class="fallback">
<input name="file" type="file" multiple />
</div>
<div id="actions" class="row">
<div class="col-lg-7">
<!-- The fileinput-button span is used to style the file input field as button -->
<span class="btn btn-success fileinput-button">
<i class="glyphicon glyphicon-plus"></i>
<span>Añadir imágenes...</span>
</span>
<button type="submit" class="btn btn-primary start" style="display: none;">
<i class="glyphicon glyphicon-upload"></i>
<span>Start upload</span>
</button>
<button type="reset" class="btn btn-warning cancel" style="display: none;">
<i class="glyphicon glyphicon-ban-circle"></i>
<span>Cancel upload</span>
</button>
</div>

<div class="col-lg-5">
<!-- The global file processing state -->
<span class="fileupload-process">
<div id="total-progress" class="progress progress-striped active" role="progressbar" aria-valuemin="0" aria-valuemax="100" aria-valuenow="0">
<div class="progress-bar progress-bar-success" style="width:0%;" data-dz-uploadprogress></div>
</div>
</span>
</div>
</div>

<div class="table table-striped files" id="previews">
<div id="template" class="file-row row">
<!-- This is used as the file preview template -->
<div class="col-xs-12 col-lg-3">
<span class="preview" style="width:160px;height:160px;">
<img data-dz-thumbnail />
</span>
<br/>
<button class="btn btn-primary start" style="display:none;">
<i class="glyphicon glyphicon-upload"></i>
<span>Empezar</span>
</button>
<button data-dz-remove class="btn btn-warning cancel">
<i class="icon-ban-circle fa fa-ban-circle"></i> 
<span>Cancelar</span>
</button>
<button data-dz-remove class="btn btn-danger delete">
<i class="icon-trash fa fa-trash"></i> 
<span>Eliminar</span>
</button>
</div>
<div class="col-xs-12 col-lg-9">
<p class="name" data-dz-name></p>
<p class="size" data-dz-size></p>
<div>
<strong class="error text-danger" data-dz-errormessage></strong>
</div>
<div>
<div class="progress progress-striped active" role="progressbar" aria-valuemin="0" aria-valuemax="100" aria-valuenow="0">
<div class="progress-bar progress-bar-success" style="width:0%;" data-dz-uploadprogress></div>
</div>
</div>
</div>
</div>
</div>

<div class="dropzone-here"><span><i class="fas fa-cloud-upload-alt fa-3x"></i></span><br>Arrastra los archivos aquí para subirlos.</div>
</form> 
</div>
</div>



<div class="footer-content row">
<div class="col-lg-12">
<div class="card-body text-center mt-4 mb-4">
<h5 class="card-title">Ver imagénes guardadas</h5>
<a href="images.php" class="btn btn-primary" style="text-align: center;">
<i class="fa fa-eye"></i> Ver Imágenes
</a>
</div>
</div>
</div>

<footer>
<hr>
<div class="copyright"> &copy; 2013 - <?=date("Y")?> <a href="https://baulcode.com" target="_blank">baulcode</a>. All rights reserved </div>
<div class="footerlogo"><a href="https://baulcode.com" target="_blank"></a> </div>
</footer>

</div>
</main>
<script>
// Get the template HTML and remove it from the doument
var previewNode = document.querySelector("#template");
previewNode.id = "";
var previewTemplate = previewNode.parentNode.innerHTML;
previewNode.parentNode.removeChild(previewNode);

var myDropzone = new Dropzone(document.body, {
url: "upload.php",
paramName: "file",
acceptedFiles: 'image/*',
maxFilesize: 2,
maxFiles: 3,
thumbnailWidth: 160,
thumbnailHeight: 160,
thumbnailMethod: 'contain',
previewTemplate: previewTemplate,
autoQueue: true,
previewsContainer: "#previews",
clickable: ".fileinput-button"
});

myDropzone.on("addedfile", function(file) {
$('.dropzone-here').hide();
// Hookup the start button
file.previewElement.querySelector(".start").onclick = function() { myDropzone.enqueueFile(file); };
});

// Update the total progress bar
myDropzone.on("totaluploadprogress", function(progress) {
document.querySelector("#total-progress .progress-bar").style.width = progress + "%";
});

myDropzone.on("sending", function(file) {
// Show the total progress bar when upload starts
document.querySelector("#total-progress").style.opacity = "1";
// And disable the start button
file.previewElement.querySelector(".start").setAttribute("disabled", "disabled");
});

// Hide the total progress bar when nothing's uploading anymore
myDropzone.on("queuecomplete", function(progress) {
//document.querySelector("#total-progress").style.opacity = "0";
});

// Setup the buttons for all transfers
// The "add files" button doesn't need to be setup because the config
// `clickable` has already been specified.
document.querySelector("#actions .start").onclick = function() {
myDropzone.enqueueFiles(myDropzone.getFilesWithStatus(Dropzone.ADDED));
};

$('#previews').sortable({
items:'.file-row',
cursor: 'move',
opacity: 0.5,
containment: "parent",
distance: 20,
tolerance: 'pointer',
update: function(e, ui){
//actions when sorting
}
});
</script>
</body>
</html>



B) Fichero: Styles.css

Aquí se almacenará nuestros estilos styles.css. Sin embargo, solo agregamos algunas clases para el presente ejemplo:

#previews {
padding: 15px;
padding-top: 0px;
padding-bottom: 0px;
margin-top: 15px;
min-height: 220px;
background-color: #fbfbfb;
border-style: dashed;
}

.dropzone-here {
text-align: center;
padding-top: 60px;
width: 100%;
position: absolute;
font-size: 18px;
font-weight: bold;
top: 50px;
}

#previews .file-row .delete {
display: none;
}

#previews .file-row.dz-success .start,
#previews .file-row.dz-success .cancel {
display: none;
}

#previews .file-row.dz-success .delete {
display: block;
}

.dz-image-preview {
border: 1px solid #d6d4d4;
padding-top: 15px;
padding-bottom: 15px;
margin-bottom: 15px;
}

.preview {
position: relative;
background: #fff;
border: 1px solid #dadada;
text-align: center;
display: table-cell;
vertical-align: middle;
}

.preview img {
cursor: pointer;
}

.progress {
border: 1px solid #ccc;
position: relative;
display: block;
height: 22px;
padding: 0;
min-width: 200px;
margin:4px 0;
background: #DEDEDE;
background: -webkit-gradient(linear, left top, left bottom, from(#ccc), to(#e9e9e9));
background: -moz-linear-gradient(top, #ccc, #e9e9e9);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#cccccc', endColorstr='#e9e9e9');
-moz-box-shadow:0 1px 0 #fff;
-webkit-box-shadow:0 1px 0 #fff;
box-shadow:0 1px 0 #fff;
-moz-border-radius: 4px;
-webkit-border-radius: 4px;
border-radius: 4px;
}

.progress-bar {
color: #ffffff;
display: block;
height: 20px;
margin: 0;
padding: 0;
text-align:center;
-moz-box-shadow:inset 0 1px 0 rgba(255,255,255,0.5);
-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.5);
box-shadow:inset 0 1px 0 rgba(255,255,255,0.5);
-moz-border-radius: 3px;
-webkit-border-radius: 3px;
border-radius: 3px;
border: 1px solid #0078a5;
background-color: #5C9ADE;
background: -moz-linear-gradient(top, #00adee 10%, #0078a5 90%);
background: -webkit-gradient(linear, left top, left bottom, color-stop(0.1, #00adee), color-stop(0.9, #0078a5));
}

El código más interesante y que ejecuta la acción se puede situar en la cabecera justo después de la llamada a las librerías o justo antes de la etiqueta de cierre </body>.

Configuracion de la libreria

La clase Dropzone posee algunos parámetros que podemos configurar a nuestro antojo y lo explicamos a continuación:

  • url
  • paramName
  • aceptedFiles
  • maxFilesize
  • maxFiles
  • thumbnailWidth
  • thumbnailHeight
  • previewTemplate
  • autoQueue
  • previewsContainter
  • clickable

Si desea más opciones de configuración puedes ver todas las opciones en la página oficial.

C) Fichero: upload.php

El archivo upload.php que se encarga de avlidar la extension del archivo y subir las imágenes al servidor, veamos su contenido.

<?php
if (($_FILES["file"]["type"] == "image/pjpeg")
|| ($_FILES["file"]["type"] == "image/jpeg")
|| ($_FILES["file"]["type"] == "image/png")
|| ($_FILES["file"]["type"] == "image/gif")) {
if (move_uploaded_file($_FILES["file"]["tmp_name"], "images/".$_FILES['file']['name'])) {
echo 'si';
} else {
echo 'no';
}
}

?>

Donde tan solo comprobamos que los archivos que se están intentando subir sean imágenes tipo pjpeg, jpeg, png o gif y con la función move_uploaded_file() movemos el archivo temporal subido a la carpeta deseada.

Además de la subida de archivos, en el ejemplo en funcionamiento estamos ofreciendo la posibilidad de poder ordenar los elementos utilizando jQuery Sortable. Lo estamos consiguiendo usando la función sortable que trae la librería jQuery UI.

D) Archivo images.php

Muestra los ficheros subidos al servidor y nos da la posibilidad de poder eliminarlos.

<?php
function showFiles($path) {
$dir = opendir($path);
$files = array();
while ($current = readdir($dir)){
if( $current != "." && $current != "..") {
if(is_dir($path.$current)) {
showFiles($path.$current.'/');
}
else {
$files[] = $current;
}
}
}
echo '<h6>'.$path.'</h6><hr>';
echo '<div class="row">';
for ($i=0; $i<count( $files ); $i++) {
echo '<div class="col-xs-12 col-lg-2 text-center" style="margin-bottom:10px;">';
echo '<span class="preview" style="width:160px;height: 160px;margin-bottom:7px;"> ';
echo '<img src="images/'.$files[$i].'" style="max-width:160px;max-height: 160px;" />';
echo '</span>';
echo '<a class="btn btn-danger" href="images.php?remove='.$files[$i].'"><i class="fas fa-trash-alt"></i> Eliminar</a>';
echo '</div>';
}
echo '</div>';
}

if (isset($_GET['remove'])) {
if (file_exists('images/'.$_GET['remove'])) {
unlink('images/'.$_GET['remove']);
}
}
?>
<!DOCTYPE html>
<html lang="es" class="h-100">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

<!-- Bootstrap CSS -->
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" integrity="sha384-JcKb8q3iqJ61gNV9KGb8thSsNjpSL0n8PARn9HuZOnIxN0hoP+VmmDGMN5t9UJ0Z" crossorigin="anonymous">
<link rel="stylesheet" href="css/styles.css">
<script src="https://code.jquery.com/jquery-3.2.1.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js"></script>
<script src="js/dropzone.js"></script>
<script src="js/jquery-ui.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.2/css/all.min.css" integrity="sha512-HK5fgLBL+xu6dm/Ii3z4xhlSUyZgTT9tuc/hSrtw6uzJOvgRr2a9jyxxT1ely+B+xFAmJKVSTbpM/CuL7qxO8w==" crossorigin="anonymous" />

<title>Dropzone múltiple subida de archivos</title>

</head>
<body class="d-flex flex-column h-100">

<header>
<!-- Fixed navbar -->
<nav class="navbar navbar-expand-md navbar-dark bg-dark">
<div class="container">
<a class="navbar-brand" href="index.php">BaulPHP</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarCollapse" aria-controls="navbarCollapse" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarCollapse">
<ul class="navbar-nav mr-auto">
<li class="nav-item active">
<a class="nav-link" href="index.php">Portada <span class="sr-only">(current)</span></a>
</li>
</ul>
<form class="form-inline mt-2 mt-md-0">
<input class="form-control mr-sm-2" type="text" placeholder="Search" aria-label="Search">
<button class="btn btn-outline-success my-2 my-sm-0" type="submit">Busqueda</button>
</form>
</div>
</div>
</nav>
</header>

<!-- Begin page content -->
<hr>
<br>
<main>
<div class="container">

<div class="row">
<div class="col-md-12">
<hr>
</div>
</div>

<div class="row">
<div class="col-md-12">
<div class="card">
<h5 class="card-header">Imagenes subidas</h5>
<div class="card-body">
<div id="content" class="col-lg-12">
<?php showFiles('images/'); ?>
</div> 
</div>
</div> 

</div>

</div>

<footer>
<hr>
<div class="copyright"> &copy; 2013 - <?=date("Y")?> <a href="https://baulcode.com" target="_blank">baulcode</a>. All rights reserved </div>
<div class="footerlogo"><a href="https://baulcode.com" target="_blank"></a> </div>
</footer>

</div>
</main>
</body>
</html>

Conclusión

Hemos aprendido a validar los archivos que se están intentando subir al servidor sean imágenes tipo:

  • pjpeg
  • jpeg
  • png
  • gif

Para poder subir los archivos nos  apoyaremos de la función move_uploaded_file() y movera a la carpeta images declarado.

Es muy liviano viendo de un aspexcto neutral ya que solo es necesario incorporar 2 archivos para implementar la subida de archivos al servidor.

Además tiene muchas opciones y eventos que lo hace muy personalizable.

Nestor Tapia

Bloggero, amante de la programación PHP, innovador y me fascina compartir información. Desde que conocí el entorno informatico y el internet me llamó la atención la programación, Por tal motivo he creado mi blog BAULPHP.COM para compartir mis experiencias con todos ustedes. ¡Gracias por leerme!.

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Botón volver arriba