Drag & Drop y redimensionado de imágenes

¡Buenas! En este primer tutorial de 2017, después de más de 2 años con el blog activo, quisiera enseñar cómo crear un drag & drop con HTML, PHP y Javascript.

El tutorial también enseñará a redimensionar y comprimir imágenes cuando estén siendo subidas, creando así miniaturas (thumbnails).

Vista previa del ejemplo:

¿Cómo crear un drag & drop?

Este desarrollo se hizo en local usando XAMPP. El único servicio activo es Apache (no requiero MySQL para este ejemplo).

Recurso: Dropzone.js

Para este ejemplo estaremos usando Dropzone.js, una librería creada por Matias Meno, que trabaja sin dependencias (como jQuery, por ejemplo).

Podemos obtener la última versión desde el repositorio de GitHub. Debemos obtener tanto el CSS como el JS.

Recurso: ImageResize.php

También usaremos una librería de PHP que sirve para redimensionar, escalar y cortar imágenes. Se puede encontrar en el repositorio de GitHub.

Su uso es muy sencillo, por lo que integrarlo va a ser un juego de niños.

Estructura de archivos

De esta manera organizaremos las carpetas básicas que necesitaremos para este proyecto.

assets
  L css
    L dropzone.css
    L styles.css
  L js
    L dropzone.js
    L main.js
uploads
   L thumbnails
ImageResize.php
index.php
upload.php
index.php

Este archivo debe tener una estructura básica de HTML5.

Primero debemos incluir los archivos styles.css y dropzone.css en el head.
En el body debemos crear un formulario, donde el action debe apuntar al archivo upload.php. Este será el archivo que va a procesar los archivos que “tiremos” en el drag and drop. También debemos definir el ID que usaremos para identificar ese formulario, y dos clases: “dropzone” y “needsclick”.

En el interior del form podemos añadir un div con las clases “dz-message” y “needsclick”, el cual será el contenedor que muestre un mensaje al usuario.

Y por último, antes de que cierre la etiqueta body añadiremos dos documentos: dropzone.js y main.js.

Nos debería quedar un documento parecido a este:

<!doctype html>
<html>
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="assets/css/styles.css">
<link rel="stylesheet" href="assets/css/dropzone.css">
<title>Drag & Drop</title>
</head>
<body>

<h1>Drag & Drop</h1>

<div id="dropzone">
    <form action="upload.php" id="form_imagen" class="dropzone needsclick">
        <div class="dz-message needsclick">
            Click para seleccionar<br>
            o arrastra el archivo
        </div>
    </form>
</div>

<script src="assets/js/dropzone.js"></script>
<script src="assets/js/main.js"></script>
</body>
</html>
main.js

Este archivo será el que enviará la imagen al archivo upload.php. Lo primero que debemos declarar es que Dropzone no busque los formularios que son para drag and drop. Esto nos evitará el error Dropzone already attached.

Después debemos crear el objeto “Dropzone”, y debemos definir el elemento que será afectado por Dropzone. En este caso, lo definimos usando el ID que le establecimos al formulario. Vamos a definir los opciones que requiramos en base a la documentación oficial. Estas opciones son de ejemplo para este tutorial.

Definimos que los archivos deben auto-procesarse en cuanto sean “tirados” en el formulario, el nombre del parámetro que recibirá PHP ($_POST['parametro']), no permitimos que haya subidas múltiples, añadimos un enlace para eliminar los archivos (es decir, cuando seleccionas o arrastras un archivo, tener la posibilidad de cancelar la subida y eliminar el archivo de la lista), establecemos que el máximo de archivos permitido a subir es 1, los archivos que aceptamos (MIME type) y la cantidad de subidas paralelas que debe permitir ese formulario.

A continuación, debemos definir el elemento de Dropzone que estamos usando a través de this, para poder acceder a él cuando recibamos el evento success (es decir, que el archivo fue subido con éxito). Es importante entender que el tipo de evento/callback que reciba Dropzone dependerá de la respuesta que devuelva el archivo PHP. Por ejemplo, si a la hora de subirlo, la imagen no cumple los parámetros que necesitamos (peso, dimensiones, etc.) debemos devolver un error 500.

Entonces, este JS debe quedar más o menos así:

Dropzone.autoDiscover = false;
	
new Dropzone('#form_imagen', {
	autoProcessQueue:true,
	paramName: "imagen-subida",
	uploadMultiple: false,
	addRemoveLinks: true,
	maxFiles: 1,
	acceptedFiles: "image/jpeg,image/jpg,image/png,image/gif",
	parallelUploads: 1,
	init: function() {
		"use strict";
		var myDropzone = this;
	  
		myDropzone.on("success", function(files,response) {
		  var respuesta = response;
		  if (respuesta.estado == "error") {
			  alert(respuesta.mensaje);
		  }
		});
	}
});
upload.php

Este archivo, como ya mencioné, será el que haga las comprobaciones de los archivos que se suban (si es imagen, si tiene el peso adecuado). También creará las miniaturas y moverá las imágenes donde corresponda.

Lo primero es incluir el archivo ImageResize.php, y usar el namespace \Eventviva\ImageResize. Después vamos a obtener el nombre de la imagen. A continuación definimos las extensiones/los tipos de archivo que permitimos. Esto se comprueba previamente desde el front (lo hace Dropzone en base a la opción acceptedFiles), pero no viene mal comprobarlo también en el back-end.

Si el tipo de archivo está bien, obtenemos el peso. Debemos definir el límite en bytes (hay que tener en cuenta el tamaño máximo de subida de archivos que esté definido en PHP, desde los atributos upload_max_filesize, post_max_size, etcétera), en mi caso lo seteo a 1,5 megabytes.

Y si está todo correcto, simplemente movemos el archivo subido, creamos la miniatura y la enviamos a la carpeta thumbnails.

<?php
include('ImageResize.php');
use \Eventviva\ImageResize;
//error_reporting(0); //Activar en producción

//Obtenemos el nombre del archivo
$nombreImagen = $_FILES['imagen-subida']['name'];
//Limpiamos el nombre 
$nombreImagen = htmlspecialchars($nombreImagen);

//Extensiones permitidas
$extensiones = array('jpg', 'gif', 'png');

//Obtenemos la extensión del archivo subido
$extension = explode('.', $nombreImagen);
$extension = end($extension);
$extension = strtolower($extension);

//Si la extensión no está dentro del array,
//no permitimos que se suba
//IMPORTANTE enviar un header de error (en este caso, 500)
if(!in_array($extension, $extensiones)) {
	header('HTTP/1.1 500 Internal Server Error');
	header('Content-Type: application/json');
	$error = array('estado' => 'error', 'mensaje' => 'Formato de imagen no válido en "'.$nombreImagen.'".');
	die(json_encode($error, JSON_FORCE_OBJECT));
}

/* ------------------- */

//Obtenemos el peso del archivo
$pesoImagen = $_FILES['imagen-subida']['size'];
//Definimos el máximo de bytes que debe permitir
$tamañoMaximoBytes = 1572864; // -> 1,5 MB

//Mostramos un mensaje si es mayor al tamaño expresado en Bytes
if($pesoImagen > $tamañoMaximoBytes) {
	header('HTTP/1.1 500 Internal Server Error');
	header('Content-Type: application/json');
	$error = array('estado' => 'error', 'mensaje' => 'El peso máximo permitido es de 1,5 MB. "'.$nombreImagen.'" pesa demasiado.');
	die(json_encode($error, JSON_FORCE_OBJECT));
}

/* ------------------- */

//Si todo está bien, movemos el archivo a "uploads"
$carpetaUploads = dirname(__FILE__)."/uploads/";

$archivoTemporal = $_FILES["imagen-subida"]["tmp_name"];
$targetFile =  $carpetaUploads.$nombreImagen;

if (move_uploaded_file($archivoTemporal,$targetFile)) {
	//Crear thumbnail
	$image = new ImageResize($targetFile);
	$image->resizeToWidth(180);
	
	if ($extension == "jpg") {
		$image->quality_jpg = 100; //La mejor calidad
	} else if ($extension == "png") {
		$image->quality_png = 9; //La mejor compresión
	}
	
	//Guardar miniatura en la carpeta correspondiente
	$image->save($carpetaUploads."thumbnails/".$nombreImagen);
	
	header("HTTP/1.1 200 OK");
	header('Content-Type: application/json');
	$datos = array('estado' => '200');
	die(json_encode($datos, JSON_FORCE_OBJECT));
} else {
	header('HTTP/1.1 500 Internal Server Error');
	header('Content-Type: application/json');
	$error = array('estado' => 'error', 'mensaje' => 'Error del servidor moviendo el archivo.');
	die(json_encode($error, JSON_FORCE_OBJECT));
}
?>

Y listo. Ya tenemos todo lo necesario para empezar a usar el sistema drag & drop que nos provee DropzoneJS.

Conclusión:

Es bastante sencillo y liviano. Es recomendable leer todas las documentaciones para entender mejor qué opciones tenemos.

Espero que el tutorial haya sido de utilidad. Nos leemos, ¡saludos!

Anuncios

4 comments

  1. Jesus Murcia Sanchez · febrero 20

    Muy buenas. Buen tutorial y todo explicado, pero tengo problemas al implementar el imageresizer.

    Podrías subir el proyecto de ejemplo para poder descargar? Gracias

    Me gusta

    • Rocanrol · febrero 21

      Buenas. ¿Qué problemas estás teniendo con imageResizer?

      Saludos.

      Me gusta

      • Jesus · febrero 21

        Perdona ya lo resolví haciendo de otra manera.

        Gracias por su atención

        Me gusta

  2. Gracias me sirvió de mucho Saludos!

    Me gusta

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s