
📤 Carga de archivos con arrastrar y soltar usando Dropzone.js y PHP
Los usuarios esperan poder subir archivos a un sitio web simplemente arrastrándolos a la ventana del navegador. El botón «Elegir archivo» y el explorador de archivos ya se perciben como pasos innecesarios. Pero programar la funcionalidad de arrastrar y soltar desde cero en JavaScript implica varios cientos de líneas de código, barras de progreso, vistas previas y manejo de errores. Afortunadamente, existe una librería lista para usar que cubre todo esto de forma inmediata.
Dropzone.js es una librería JavaScript de código abierto para la carga de archivos mediante arrastrar y soltar. Muestra vistas previas de imágenes, una barra de progreso y no requiere jQuery. Funciona con cualquier lenguaje del lado del servidor: PHP, Node.js, Python. En esta guía encontrará el ciclo completo, desde la instalación hasta la configuración en producción, en seis pasos.
💡 Resumen rápido:
- Qué hace: la librería Dropzone.js convierte cualquier elemento HTML en una zona de colocación de archivos con envío AJAX al servidor.
- Cómo instalarla: un enlace CDN (unpkg o jsDelivr) o un paquete npm; ambas opciones se tratan en el primer paso.
- Cómo manejarla en el servidor: un script PHP recibe el archivo a través de
$_FILES, igual que en un formulario convencional; el tercer paso incluye código listo para usar. - Dónde aplicarla: formularios de contacto con adjuntos, carga de avatares, importación masiva de imágenes a una galería de WordPress.
Paso 1: Instalar Dropzone.js
La forma más rápida es incluir la librería mediante CDN. Añada esta etiqueta al <head> de su página:
1 <script src="https://unpkg.com/dropzone@5/dist/min/dropzone.min.js"></script>
El archivo CSS se incluye por separado:
1 <link rel="stylesheet" href="https://unpkg.com/dropzone@5/dist/min/dropzone.min.css" />
Si utiliza un empaquetador, instálela mediante npm:
1 npm install dropzone
E impórtela en su archivo JS:
1 import Dropzone from "dropzone";
Tenga en cuenta que la versión 5.9 es la última estable a principios de 2026. La versión seis está en fase beta y aún no se recomienda para producción. El repositorio del proyecto se encuentra ahora en github.com/dropzone/dropzone (anteriormente enyo/dropzone).
Paso 2: Marcado HTML básico e inicialización
Cree un formulario con la clase dropzone. Dropzone lo detectará automáticamente y lo convertirá en una zona de colocación:
1 <form action="/upload.php" 2 class="dropzone" 3 id="my-dropzone"></form>
Eso es todo. Cuando un usuario arrastre un archivo a este formulario, la librería lo enviará mediante una petición AJAX a la URL indicada en el atributo action. En el lado del servidor, el archivo llegará en $_FILES['file'], exactamente igual que con un envío convencional a través de <input type="file">.

Si necesita una alternativa para navegadores sin JavaScript, añada un campo normal dentro del formulario:
1 <form action="/upload.php" class="dropzone"> 2 <input name="file" type="file" multiple /> 3 </form>
El botón «Choose file» solo se mostrará cuando JavaScript no esté disponible. Con JS habilitado, Dropzone lo ocultará y mostrará su propia zona de arrastre.
Paso 3: Gestión de archivos en el servidor
Un script PHP recibe las subidas igual que un formulario normal. Esta es la versión funcional mínima:
1 <?php 2 if (!empty($_FILES)) { 3 $uploadDir = __DIR__ . '/uploads/'; 4 5 if (!is_dir($uploadDir)) { 6 mkdir($uploadDir, 0755, true); 7 } 8 9 $originalName = basename($_FILES['file']['name']); 10 $targetPath = $uploadDir . time() . '_' . $originalName; 11 12 if (move_uploaded_file($_FILES['file']['tmp_name'], $targetPath)) { 13 http_response_code(200); 14 echo json_encode(['status' => 'ok', 'file' => $targetPath]); 15 } else { 16 http_response_code(500); 17 echo json_encode(['status' => 'error', 'message' => 'Failed to save file']); 18 } 19 }
Qué ocurre aquí:
- El script crea una carpeta
uploads/si no existe. - Se añade
time()al nombre original del archivo, lo que evita conflictos de nombres. basename()elimina cualquier ataque de path-traversal (intentos de escapar de la carpeta mediante../).- Dropzone espera un HTTP 200 del servidor; de lo contrario, considera que la subida falló y muestra un error.
Para producción, añada validación de extensión y tipo MIME:
1 $allowed = ['jpg', 'jpeg', 'png', 'gif', 'pdf', 'zip']; 2 $ext = strtolower(pathinfo($originalName, PATHINFO_EXTENSION)); 3 4 if (!in_array($ext, $allowed)) { 5 http_response_code(400); 6 echo json_encode(['status' => 'error', 'message' => 'Invalid file type']); 7 exit; 8 }
Paso 4: Limitar el número, tamaño y tipo de archivos
Los tres ajustes de Dropzone que más se necesitan se configuran mediante un objeto de configuración JavaScript:
1 Dropzone.options.myDropzone = { 2 maxFiles: 10, 3 maxFilesize: 5, // In megabytes 4 acceptedFiles: 'image/*,.pdf', 5 dictDefaultMessage: 'Drag files here or click to select' 6 };
Desglose de opciones:
- maxFiles: cuántos archivos puede subir el usuario de una vez. Por defecto no hay límite; en el ejemplo, no más de 10.
- maxFilesize: tamaño máximo de un solo archivo en megabytes. Un valor de 5 significa 5 MB. En versiones antiguas de la librería el valor por defecto era 256 MB; en la actual 5.x también es 256, así que especifique explícitamente su propio límite.
- acceptedFiles: una cadena de tipos MIME separados por comas. El patrón
image/*permite cualquier imagen,.pdfañade PDF a la lista. Consulte la lista completa de tipos MIME en la documentación de Dropzone. - dictDefaultMessage: el texto que el usuario ve en la zona de subida vacía (en inglés en este ejemplo).
La clave myDropzone en Dropzone.options.myDropzone es la versión en camelCase del atributo HTML id="my-dropzone". Los guiones se eliminan y cada palabra subsiguiente se escribe con mayúscula inicial: my-dropzone → myDropzone, file-upload-area → fileUploadArea.
Las opciones resizeWidth y resizeHeight le permiten reducir el tamaño de las imágenes en el cliente antes de enviarlas. Esto ahorra ancho de banda al usuario y reduce la carga del servidor, especialmente cuando los visitantes suben fotos del teléfono de 10-15 MB cada una. Especifique ambos parámetros juntos:
1 Dropzone.options.myDropzone = { 2 resizeWidth: 1200, 3 resizeHeight: 1200, 4 resizeQuality: 0.8, 5 resizeMethod: 'contain' 6 };
El método contain ajusta la imagen dentro de los límites dados sin recortar, mientras que crop recorta a las dimensiones exactas. Una calidad de 0.8 es prácticamente indistinguible del original, pero reduce el tamaño del archivo entre 4 y 7 veces. Importante: el redimensionamiento solo funciona con archivos de imagen; los PDF y archivos comprimidos se transfieren tal cual. El navegador realiza el redimensionamiento mediante la API Canvas, por lo que no se necesitan librerías adicionales.
Paso 5: Envío manual de archivos
Por defecto, Dropzone envía un archivo al servidor inmediatamente después de añadirlo. Esto no siempre es conveniente: a veces necesita que el usuario rellene campos del formulario junto a la zona de subida y envíe todo con un solo botón.
Desactive la subida automática con el indicador autoProcessQueue: false y vincule el envío a un botón externo:
1 Dropzone.options.myDropzone = { 2 autoProcessQueue: false, 3 maxFilesize: 5, 4 init: function () { 5 const myDropzone = this; 6 const submitBtn = document.querySelector('#btnUpload'); 7 8 submitBtn.addEventListener('click', function () { 9 if (myDropzone.getQueuedFiles().length === 0) { 10 alert('No files to upload'); 11 return; 12 } 13 myDropzone.processQueue(); 14 }); 15 16 this.on('success', function (file, response) { 17 console.log('File uploaded:', file.name); 18 }); 19 } 20 };
El punto clave aquí es el método processQueue(). Este activa el envío de todos los archivos acumulados en la cola. El evento success se dispara para cada archivo individualmente, lo que le permite actualizar la interfaz o guardar el ID del archivo subido en un campo oculto del formulario.
Paso 6: Enviar datos adicionales junto con el archivo
A veces necesita adjuntar parámetros de servicio a una subida: ID de usuario, estado del documento, categoría seleccionada. Dropzone tiene el evento sending para esto:
1 Dropzone.options.myDropzone = { 2 init: function () { 3 this.on('sending', function (file, xhr, formData) { 4 formData.append('status', 'draft'); 5 formData.append('user_id', currentUserId); 6 }); 7 8 this.on('success', function (file, responseText) { 9 console.log('Done:', responseText); 10 }); 11 } 12 };
El evento sending se llama justo antes de que se envíe el XMLHttpRequest. El tercer argumento formData es un objeto FormData estándar al que puede añadir cualquier par clave-valor mediante append(). En el lado del servidor, estos parámetros se leen desde $_POST['status'] y $_POST['user_id'].
Escenario práctico: el usuario selecciona un álbum de un menú desplegable junto al área de Dropzone, y el ID del álbum se envía con cada archivo. El servidor clasifica inmediatamente las subidas en las carpetas correspondientes.
Seguridad: tres reglas que no puede ignorar
Antes de poner Dropzone en producción, compruebe tres cosas.
Primero. Valide siempre el tipo y tamaño del archivo en el lado del servidor, incluso si ya ha configurado acceptedFiles y maxFilesize en JS. Las restricciones del lado del cliente se pueden eludir en un minuto mediante la consola del navegador. El filtrado en el servidor es la única protección real.
Segundo. Almacene los archivos subidos fuera de la raíz de documentos o en una carpeta con la ejecución de scripts deshabilitada. Por ejemplo, esta estructura:
1 /public_html ← document root ( index.php ) 2 /uploads ← folder with uploaded files (outside web access)
Entonces una URL directa al archivo no funcionará; el navegador sirve el archivo solo a través de un script PHP intermediario que verifica los permisos de acceso.
Tercero. Genere nombres de archivo únicos en el servidor. No confíe en el nombre que envía el cliente: puede contener ../, bytes nulos o caracteres especiales. basename() + time() + una cadena aleatoria es el nivel mínimo.
Cuarto. Verifique el contenido del archivo, no solo la extensión. Un atacante puede renombrar shell.php a photo.jpg y eludir un filtro basado en pathinfo(). Un enfoque fiable: determine el tipo MIME real mediante finfo_file() (el módulo Fileinfo integrado en PHP) y compárelo contra una lista blanca. Para imágenes, verifique adicionalmente que getimagesize() devuelva dimensiones válidas; esto descarta archivos corruptos y scripts camuflados. En proyectos de alto tráfico, considere delegar las subidas a un microservicio separado detrás de un proxy inverso que reciba el flujo, lo analice con antivirus (por ejemplo, ClamAV) y solo entonces lo pase a la aplicación principal.
El breve video de demostración arriba muestra todo el proceso en acción: marcado HTML, manejador PHP y el resultado en el navegador en tres minutos.
⁉️🤔 Preguntas frecuentes
¿Funciona Dropzone.js sin jQuery?
Sí, la biblioteca es completamente independiente de jQuery. A partir de la versión 5.x, se eliminaron todas las dependencias; consulte la sección de instalación en dropzone.dev. Dicho esto, existe un envoltorio jQuery independiente para quienes prefieren
$('.dropzone').dropzone(); se encuentra en el mismo paquete npm, endist/min/dropzone-jquery.min.js.
¿Cómo cambio el texto de error cuando un archivo es demasiado grande?
Utilice la opción
dictFileTooBig. Ejemplo:dictFileTooBig: 'File is too big. Maximum size is {{maxFilesize}} MB'. El marcador{{maxFilesize}}inserta automáticamente el valor de su configuración. La lista completa de claves de diccionario para localización está en la documentación.
¿Se pueden subir archivos en fragmentos para grandes volúmenes?
Sí, Dropzone v5 tiene soporte integrado para cargas fragmentadas mediante las opciones
chunking: true,chunkSize(en bytes) yforceChunking. El servidor debe ser capaz de reensamblar los fragmentos: recibir cada pieza con las cabecerasdzchunkindexydztotalchunkcount, guardarlas temporalmente y fusionarlas tras recibir la última. En el repositorio de Dropzone hay disponible un ejemplo listo para usar en PHP para el ensamblaje de fragmentos.
¿Cómo elimino la vista previa de un archivo después de una carga exitosa?
Llame a
myDropzone.removeFile(file)en el manejador del eventosuccess. El archivo desaparecerá de la interfaz, pero permanecerá en el servidor. Si también necesita eliminarlo del servidor, añada una petición AJAX en el mismo manejador antes deremoveFile.
¿Es Dropzone compatible con WordPress?
Sí, la biblioteca se puede incluir mediante
wp_enqueue_script()en su tema o plugin. Un escenario típico en WordPress: sustituir el cargador de medios estándar en una página de administración personalizada o en un formulario del frontend. Solo necesita especificar la URL del manejador correcto, normalmenteadmin_url('admin-ajax.php')con una acción AJAX registrada.
¿Es Dropzone.js adecuado para su proyecto?
Si necesita cargas de archivos arrastrando y soltando «aquí y ahora», Dropzone.js resuelve la tarea más rápido que cualquier código personalizado. He aquí cinco razones por las que la biblioteca sigue siendo relevante en 2026:
- Sin dependencias. Nada de jQuery, Bootstrap ni React. Incluya un archivo JS y funciona.
- Soporte multinavegador. Compatible con navegadores hasta IE11 y se degrada correctamente sin JavaScript.
- Visualización lista para usar. Vistas previas de imágenes, barras de progreso e iconos de éxito/error integrados, sin trabajo de maquetación CSS.
- Documentación. GitBook actualizado y un repositorio GitHub activo (más de 1700 estrellas en 2026).
- Flexibilidad. Desde el simple redimensionamiento de imágenes antes de la carga hasta cargas fragmentadas de archivos de gigabytes, todo es configurable.
Cuándo Dropzone no es la mejor opción: si ya está usando React/Vue y quiere un componente nativo sin manipulación externa del DOM. El ecosistema React tiene react-dropzone, Vue tiene vue-dropzone. Pero para JavaScript vainilla, proyectos jQuery y sitios WordPress, Dropzone.js es una herramienta probada que no le defraudará.
Ejemplo práctico de integración. Un escenario típico de WordPress: una página «Subir documentos» en un portal de clientes. El usuario arrastra escaneos de un pasaporte, contrato y fotos de la propiedad. Dropzone se incluye mediante wp_enqueue_script() en el functions.php del tema, y el manejador es una acción AJAX personalizada registrada con wp_ajax_nopriv_. Cada archivo subido con éxito se añade a la biblioteca de medios mediante wp_insert_attachment(), y su ID se guarda en los metadatos del usuario. Todo el código del frontend cabe en 30 líneas, y el del lado del servidor en 50. A modo de comparación, un arrastrar y soltar personalizado con las mismas capacidades requeriría entre 400 y 600 líneas de JavaScript y de 2 a 3 días de desarrollo.
En cuanto a las alternativas: Uppy (de los creadores de Transloadit) ofrece una arquitectura modular con plugins para React, Svelte y cargas a S3, pero requiere más dependencias y configuración. Fine Uploader no recibe mantenimiento desde 2018; existen bifurcaciones, pero sin garantías de seguridad. Dropzone.js gana en el equilibrio de «prestaciones por unidad de complejidad»: un archivo, configuración mínima y resultados visibles cinco minutos después de la integración.
Algunas capacidades adicionales que vale la pena conocer antes de empezar. Dropzone proporciona un ciclo completo de eventos: addedfile, thumbnail, uploadprogress, complete y queuecomplete. Este último es especialmente útil: se dispara cuando todos los archivos de la cola se han procesado, lo que le permite mostrar al usuario un mensaje de resumen o redirigirlo a otra página. Para fallos de red, existe un reintento integrado: ante un error de carga, Dropzone muestra un botón «Reintentar» junto a la vista previa del archivo, y el reintento pasa por el mismo manejador sin duplicar código. La interfaz es totalmente localizable mediante las opciones dict: dictCancelUpload, dictRemoveFile, dictMaxFilesExceeded y alrededor de dos docenas de claves más. El soporte para el idioma español se configura en cinco minutos copiando un objeto de la documentación. La biblioteca pesa 28 KB comprimidos (gzip), lo que añade menos de 0,1 segundos al tiempo de carga de la página en 4G móvil. Todas estas características hacen de Dropzone.js el estándar de facto para cargas arrastrando y soltando en proyectos sin frameworks: un millón de descargas npm semanales a principios de 2026, y uso en paneles de administración de WordPress, Laravel y Symfony.



