Botón HTML a medida para subir archivos con arrastrar y soltar
Las subidas de archivos son esenciales para muchas aplicaciones web, pero el campo de archivo predeterminado de HTML suele carecer de una experiencia de usuario moderna. En esta guía te mostramos cómo crear un botón HTML a medida para subir archivos con arrastrar y soltar, y así mejorar la interfaz de tu aplicación web y agilizar las interacciones de los usuarios.
Veamos cómo crear en HTML un botón de subida de archivos a medida que admita arrastrar y soltar.
Introducción a los botones de subida de archivos en HTML
El elemento <input type="file"> estándar de HTML permite a los usuarios seleccionar archivos, pero deja
poco margen para la personalización. Si creas tu propio botón de subida de archivos e integras la
función de arrastrar y soltar, puedes ofrecer una experiencia de subida más atractiva e intuitiva.
Configuración básica de subida de archivos en HTML
Empieza con un formulario HTML sencillo para subir archivos:
<form id="upload-form" action="/upload" method="post" enctype="multipart/form-data">
<input type="file" id="file-input" name="file" />
<button type="submit">Upload</button>
</form>
Este formulario funciona, pero carece de atractivo visual y de elementos de interacción modernos.
Creación de un botón de subida de archivos a medida en HTML
Mejora el formulario ocultando el campo de archivo predeterminado y usando un elemento label para activar el diálogo de selección de archivos:
<form id="upload-form" action="/upload" method="post" enctype="multipart/form-data">
<label for="file-input" id="file-input-label">
<input type="file" id="file-input" name="file" />
Choose a file
</label>
<button type="submit">Upload</button>
</form>
En este fragmento de código:
- El elemento
inputse oculta visualmente con CSS. - El
labelestá vinculado alinputmediante el atributofor, por lo que al hacer clic en él se abre el diálogo de archivos.
Aplicar estilos al botón de subida de archivos a medida con CSS
Aplica el siguiente CSS para dar estilo al label como un botón:
#file-input {
position: absolute;
width: 1px;
height: 1px;
clip-path: inset(50%);
overflow: hidden;
}
#file-input-label:focus-within {
outline: 2px solid currentColor;
outline-offset: 3px;
}
#file-input-label {
display: inline-block;
padding: 10px 20px;
background-color: #007bff;
color: #fff;
cursor: pointer;
border-radius: 4px;
font-size: 16px;
}
#file-input-label:hover {
background-color: #0056b3;
}
Este CSS oculta el campo de archivo predeterminado, da estilo al label y añade un efecto al pasar el cursor para mejorar la interacción del usuario.
Añadir la función de subida de archivos con arrastrar y soltar
Una interfaz de arrastrar y soltar mejora aún más la usabilidad. Primero, añade a tu HTML una zona para soltar archivos:
<div id="drop-area">
<p>Drag & drop files here</p>
<p>or</p>
<label for="file-input" id="file-input-label">
<input type="file" id="file-input" name="file" form="upload-form" />
Choose a file
</label>
<div id="progress-bar" class="progress-bar"></div>
</div>
<form id="upload-form" action="/upload" method="post" enctype="multipart/form-data">
<button type="submit">Upload</button>
</form>
Aplicar estilos a la zona para soltar archivos con CSS
#drop-area {
border: 2px dashed #007bff;
border-radius: 4px;
padding: 20px;
text-align: center;
}
#drop-area.highlight {
background-color: #e9ecef;
}
.progress-bar {
width: 100%;
height: 10px;
background-color: #f0f0f0;
border-radius: 5px;
margin-top: 10px;
}
.progress-bar-fill {
height: 100%;
background-color: #007bff;
border-radius: 5px;
width: 0;
transition: width 0.3s ease-in-out;
}
Este CSS define la apariencia de la zona para soltar archivos, ofrece retroalimentación visual durante los eventos de arrastre y da estilo a la barra de progreso para el seguimiento de la subida.
Implementar arrastrar y soltar con JavaScript
El siguiente código JavaScript gestiona los eventos de arrastrar y soltar, actualiza la barra de progreso y envía los archivos al servidor:
const dropArea = document.getElementById('drop-area')
const fileInput = document.getElementById('file-input')
const progressBar = document.getElementById('progress-bar')
// Prevent default behaviors
;['dragenter', 'dragover', 'dragleave', 'drop'].forEach((eventName) => {
dropArea.addEventListener(eventName, preventDefaults, false)
})
function preventDefaults(e) {
e.preventDefault()
e.stopPropagation()
}
// Highlight drop area when a file is dragged over it
;['dragenter', 'dragover'].forEach((eventName) => {
dropArea.addEventListener(eventName, highlight, false)
})
;['dragleave', 'drop'].forEach((eventName) => {
dropArea.addEventListener(eventName, unhighlight, false)
})
function highlight() {
dropArea.classList.add('highlight')
}
function unhighlight() {
dropArea.classList.remove('highlight')
}
// Handle dropped files
dropArea.addEventListener('drop', handleDrop, false)
function handleDrop(e) {
const dt = e.dataTransfer
const files = dt.files
handleFiles(files)
}
function updateProgress(percent) {
const progressBarFill =
progressBar.querySelector('.progress-bar-fill') || document.createElement('div')
progressBarFill.className = 'progress-bar-fill'
progressBarFill.style.width = `${percent}%`
if (!progressBar.contains(progressBarFill)) {
progressBar.appendChild(progressBarFill)
}
}
function handleFiles(files) {
const formData = new FormData()
for (let i = 0; i < files.length; i++) {
formData.append('file', files[i])
}
uploadFiles(formData)
}
function uploadFiles(formData) {
const xhr = new XMLHttpRequest()
xhr.upload.addEventListener('progress', (e) => {
if (e.lengthComputable) {
const percent = (e.loaded / e.total) * 100
updateProgress(percent)
}
})
xhr.addEventListener('load', () => {
if (xhr.status === 200) {
console.log('Upload completed')
} else {
console.error('Upload failed:', xhr.statusText)
}
})
xhr.addEventListener('error', () => {
console.error('Upload failed')
})
xhr.open('POST', '/upload')
xhr.send(formData)
}
Este script evita el comportamiento predeterminado del navegador, resalta la zona para soltar archivos durante los eventos de arrastre, procesa los archivos soltados y gestiona el progreso de la subida.
Gestionar las subidas de archivos en el lado del servidor
Cuando procesas archivos subidos en el servidor, necesitas una solución robusta. El siguiente ejemplo usa Node.js y Express con Multer para gestionar los archivos. Instala dependencias con mantenimiento activo:
npm install express@^4.21.2 multer@^2.3.0 cors
Este ejemplo local no es un servicio autenticado. Antes de desplegarlo, añade autorización y validación de contenido, y mantén el directorio de subidas fuera de cualquier directorio que se sirva como archivos estáticos.
const express = require('express')
const multer = require('multer')
const path = require('path')
const cors = require('cors')
const app = express()
// Configure CORS for production environments
app.use(
cors({
origin: process.env.ALLOWED_ORIGIN || 'http://localhost:3000',
methods: ['GET', 'POST'],
allowedHeaders: ['Content-Type'],
}),
)
// Multer creates a string destination if needed; never serve this directory directly.
const storage = multer.diskStorage({
destination: 'uploads/',
filename: (req, file, cb) => {
const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() * 1e9)
cb(null, file.fieldname + '-' + uniqueSuffix + path.extname(file.originalname))
},
})
const upload = multer({
storage: storage,
limits: {
fileSize: 5 * 1024 * 1024, // 5MB limit
files: 10,
},
fileFilter: (req, file, cb) => {
// Allowed file types: jpeg, jpg, png, gif
const mimetype = ['image/jpeg', 'image/png', 'image/gif'].includes(file.mimetype)
const extname = ['.jpg', '.jpeg', '.png', '.gif'].includes(path.extname(file.originalname).toLowerCase())
if (mimetype && extname) {
return cb(null, true)
}
const error = new Error('Only image files are allowed')
error.code = 'UNSUPPORTED_FILE_TYPE'
cb(error)
},
})
app.post('/upload', (req, res) => {
upload.array('file')(req, res, (err) => {
if (err instanceof multer.MulterError || err?.code === 'UNSUPPORTED_FILE_TYPE') {
return res.status(400).json({ error: 'File type, size, or count is not allowed' })
} else if (err) {
return res.status(500).json({ error: 'Could not store the upload' })
}
if (!req.files || req.files.length === 0) {
return res.status(400).json({ error: 'No files uploaded.' })
}
res.json({ message: 'Files uploaded successfully!' })
})
})
app.listen(3000, '127.0.0.1', () => {
console.log('Server started on http://localhost:3000')
})
Este ejemplo aplica límites de tamaño y rechaza con un error de cliente los tipos declarados y las extensiones no compatibles. Los tipos MIME y los nombres de archivo son metadatos controlados por el cliente, no una prueba del contenido del archivo. Valida los datos reales antes de procesarlos o servirlos.
Buenas prácticas de seguridad para la subida de archivos
Cuando gestiones subidas, ten en cuenta estas medidas clave:
- Valida los archivos tanto en el lado del cliente como en el del servidor, comprobando los tipos y los tamaños de archivo.
- Almacena los archivos fuera de la raíz web, usa nombres de archivo aleatorios y aplica permisos de archivo estrictos.
- Establece límites en el tamaño de los archivos y en las subidas simultáneas para evitar abusos.
- Usa configuraciones seguras, como HTTPS, ajustes de CORS adecuados y políticas de seguridad de contenido.
- Implementa análisis antivirus para detectar y poner en cuarentena los archivos maliciosos.
Si sigues estas prácticas, garantizarás un sistema de subida de archivos seguro y robusto.
Conclusión
Crear un botón HTML a medida para subir archivos con arrastrar y soltar no solo mejora la experiencia de usuario, sino que también te permite integrar funciones esenciales como el seguimiento del progreso y una seguridad reforzada. Para requisitos de subida más avanzados, como soporte para TypeScript, indicadores de progreso detallados, vistas previas de archivos e integración con almacenamiento en la nube, considera usar Uppy, un cargador de archivos de código abierto que cubre las necesidades del desarrollo moderno.
Ahora ya sabes cómo añadir en HTML un botón de subida de archivos a medida y mejorarlo con arrastrar y soltar. ¡Feliz programación!
