Formulario de subida de archivos HTML: guía para desarrolladores
La funcionalidad de subida de archivos es esencial para las aplicaciones web modernas. Este tutorial te guía en la creación de un formulario HTML de subida de archivos seguro y fácil de usar, con funciones como la selección de varios archivos, el seguimiento del progreso y el soporte para arrastrar y soltar.
Introducción a los formularios de subida de archivos en HTML
Los formularios de subida de archivos en HTML permiten a las personas seleccionar archivos y subirlos a un servidor. Las aplicaciones web modernas necesitan capacidades sólidas de subida de archivos para funciones como fotos de perfil, envío de documentos y uso compartido de medios. Veamos cómo implementar esta funcionalidad de forma eficaz.
Cómo configurar un formulario básico de subida de archivos en HTML
Crea un formulario HTML básico de subida de archivos con el elemento <form> y un campo <input type="file">:
<form action="/upload" method="post" enctype="multipart/form-data">
<label for="file-upload" class="form-label">Select file:</label>
<input
type="file"
id="file-upload"
name="file"
accept=".jpg,.jpeg,.png,.pdf"
aria-describedby="file-help"
/>
<div id="file-help" class="form-text">Accepted formats: JPG, PNG, PDF (max 10MB)</div>
<button type="submit">Upload File</button>
</form>
Atributos clave:
enctype="multipart/form-data"habilita las subidas de archivos- el atributo
acceptlimita los tipos de archivo - los atributos ARIA mejoran la accesibilidad
Manejo de subidas de uno o varios archivos
Habilita la selección de varios archivos e implementa el seguimiento del progreso:
<form id="upload-form" action="/upload" method="post" enctype="multipart/form-data">
<label for="file-upload">Select files:</label>
<input type="file" id="file-upload" name="files[]" multiple accept=".jpg,.jpeg,.png,.pdf" />
<div id="upload-progress" class="progress" hidden>
<div class="progress-bar" role="progressbar" aria-label="Upload progress" aria-valuemin="0" aria-valuemax="100"></div>
</div>
<button type="submit">Upload Files</button>
</form>
<script>
const form = document.getElementById('upload-form')
const progress = document.getElementById('upload-progress')
const progressBar = progress.querySelector('.progress-bar')
form.addEventListener('submit', async (e) => {
e.preventDefault()
const formData = new FormData(form)
progress.hidden = false
try {
await new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest()
xhr.upload.addEventListener('progress', (event) => {
if (event.lengthComputable) {
const percent = (event.loaded / event.total) * 100
progressBar.style.width = percent + '%'
progressBar.setAttribute('aria-valuenow', String(percent))
}
})
xhr.onload = () => {
if (xhr.status >= 200 && xhr.status < 300) resolve()
else reject(new Error('Server rejected the upload'))
}
xhr.onerror = () => reject(new Error('Network error'))
xhr.onabort = () => reject(new Error('Upload canceled'))
xhr.ontimeout = () => reject(new Error('Upload timed out'))
xhr.open('POST', form.action)
xhr.timeout = 30000
xhr.send(formData)
})
console.log('Upload successful')
} catch (error) {
console.error('Upload failed:', error)
alert('Upload failed. Please try again.')
} finally {
progress.hidden = true
}
})
</script>
Consideraciones:
- Para archivos muy grandes, considera implementar subidas por fragmentos y una opción para cancelar la subida.
- Para uso en producción, asegúrate de que el servidor admita el seguimiento del progreso y la validación de archivos.
Personalización del botón de subida de archivos
Crea una interfaz de subida de archivos moderna y con estilo:
<div class="upload-container">
<button type="button" id="choose-files" class="upload-button">
<svg aria-hidden="true" width="24" height="24">
<path d="M12 2l4 4h-3v9h-2V6H8l4-4z" />
</svg>
Choose Files
</button>
<input type="file" id="file-upload" multiple hidden />
<div id="file-list" class="file-list"></div>
</div>
<style>
.upload-button {
display: inline-flex;
align-items: center;
gap: 8px;
padding: 8px 16px;
background: #0066cc;
color: white;
border-radius: 4px;
cursor: pointer;
transition: background 0.2s;
}
.upload-button:hover {
background: #0052a3;
}
.file-list {
margin-top: 16px;
padding: 8px;
border: 1px solid #ddd;
border-radius: 4px;
}
</style>
<script>
const fileInput = document.getElementById('file-upload')
const fileList = document.getElementById('file-list')
document.getElementById('choose-files').addEventListener('click', () => fileInput.click())
function updateFileList() {
fileList.innerHTML = ''
Array.from(fileInput.files).forEach((file) => {
const item = document.createElement('div')
item.textContent = `${file.name} (${formatFileSize(file.size)})`
fileList.appendChild(item)
})
}
fileInput.addEventListener('change', updateFileList)
function formatFileSize(bytes) {
if (bytes === 0) return '0 B'
const k = 1024
const sizes = ['B', 'KiB', 'MiB', 'GiB']
const i = Math.floor(Math.log(bytes) / Math.log(k))
return `${(bytes / Math.pow(k, i)).toFixed(1)} ${sizes[i]}`
}
</script>
Cómo añadir validación del lado del cliente
Sustituye el detector change anterior por este manejador de validación y conserva los
helpers updateFileList() y formatFileSize() existentes. Las selecciones no válidas también borran
la lista de archivos mostrada. La validación del lado del servidor sigue siendo necesaria:
<script>
const MAX_FILE_SIZE = 10 * 1024 * 1024 // 10MB
const ALLOWED_TYPES = {
'image/jpeg': ['.jpg', '.jpeg'],
'image/png': ['.png'],
'application/pdf': ['.pdf'],
}
function validateFile(file) {
const errors = []
if (file.size > MAX_FILE_SIZE) {
errors.push(`File ${file.name} exceeds 10MB limit`)
}
if (!Object.keys(ALLOWED_TYPES).includes(file.type)) {
errors.push(`File ${file.name} has unsupported type: ${file.type}`)
}
return errors
}
fileInput.addEventListener('change', () => {
const errors = []
Array.from(fileInput.files).forEach((file) => {
errors.push(...validateFile(file))
})
if (errors.length > 0) {
alert(errors.join('\n'))
fileInput.value = ''
updateFileList()
return
}
// Proceed with valid files
updateFileList()
})
</script>
Implementación de la subida de archivos con arrastrar y soltar
Añade una funcionalidad moderna de arrastrar y soltar con accesibilidad y manejo de errores mejorados:
<div
id="drop-zone"
class="drop-zone"
role="button"
tabindex="0"
aria-label="Drop files here or click to select"
>
<div class="drop-zone-content">
<svg aria-hidden="true" width="48" height="48">
<path d="M24 4l8 8h-6v18h-4V12h-6l8-8z" />
</svg>
<p>Drop files here or click to select</p>
</div>
</div>
<style>
.drop-zone {
padding: 24px;
border: 2px dashed #ccc;
border-radius: 8px;
text-align: center;
transition: border-color 0.2s;
cursor: pointer;
}
.drop-zone.drag-over {
border-color: #0066cc;
background: rgba(0, 102, 204, 0.1);
}
</style>
<script>
const dropZone = document.getElementById('drop-zone')
function handleDrop(e) {
e.preventDefault()
e.stopPropagation()
try {
const dt = e.dataTransfer
const files = dt.files
handleFiles(files)
} catch (error) {
alert('An error occurred during file drop: ' + error.message)
} finally {
dropZone.classList.remove('drag-over')
}
}
function handleFiles(files) {
const errors = []
Array.from(files).forEach((file) => {
errors.push(...validateFile(file))
})
if (errors.length > 0) {
alert(errors.join('\n'))
return
}
// Update file input and list
const transfer = new DataTransfer()
Array.from(files).forEach((file) => transfer.items.add(file))
fileInput.files = transfer.files
updateFileList()
}
// Event listeners
dropZone.addEventListener('dragover', (e) => {
e.preventDefault()
dropZone.classList.add('drag-over')
})
dropZone.addEventListener('dragleave', () => {
dropZone.classList.remove('drag-over')
})
dropZone.addEventListener('drop', handleDrop)
// Keyboard accessibility
dropZone.addEventListener('keydown', (e) => {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault()
fileInput.click()
}
})
// Click also covers pointer activation on touch devices.
dropZone.addEventListener('click', () => {
fileInput.click()
})
</script>
Consideraciones de seguridad
También es esencial validar y sanear las subidas de archivos en el lado del servidor. Aplica restricciones de tipos de archivo, limita el tamaño de los archivos tanto en el cliente como en el servidor y sanea los nombres de archivo para evitar posibles vulnerabilidades de seguridad. Además, considera integrar análisis antivirus, usar HTTPS para la transmisión de archivos y mantener actualizado el software de tu servidor.
Conclusión
Este tutorial muestra cómo crear un formulario de subida de archivos moderno y accesible, con mejoras como el seguimiento del progreso, la validación del lado del cliente y el soporte para arrastrar y soltar. Para archivos muy grandes, considera implementar subidas por fragmentos y una opción para cancelar la subida, de modo que mejores la experiencia de uso y la resiliencia. Además, realiza siempre validaciones y comprobaciones de seguridad sólidas del lado del servidor.
Para una solución más robusta, con subidas reanudables y un seguimiento completo del progreso, considera usar Uppy 4.x, una herramienta de subida de archivos de código abierto que funciona a la perfección con las aplicaciones web modernas.
