Formulario multipart/form-data: todo lo que necesitas saber para subir archivos
Introducción
Cuando un usuario necesita enviar archivos (imágenes, PDFs, videos, etc.) a un servidor, el tipo de codificación multipart/form-data es el estándar recomendado. A diferencia de application/x-www-form-urlencoded, que codifica los datos como una cadena de texto, multipart/form-data divide la petición en partes independientes, cada una con su propio encabezado y contenido binario.
¿Cómo funciona multipart/form-data?
Al enviar el formulario, el navegador genera una petición HTTP POST cuya cabecera incluye:
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW
El boundary actúa como delimitador entre cada campo del formulario. Cada parte contiene:
- Content-Disposition: nombre del campo y, si es un archivo, su nombre original.
- Content-Type (opcional): tipo MIME del archivo.
- Los datos en bruto del campo o del archivo.
Implementación básica en HTML
Para que el navegador use multipart/form-data basta con definir el atributo enctype del form y añadir un input de tipo file:
<form action="/upload" method="post" enctype="multipart/form-data">
<label for="avatar" class="form-label">Selecciona tu avatar:</label>
<input type="file" name="avatar" id="avatar" class="form-control" accept="image/*" required>
<button type="submit" class="btn btn-primary mt-3">Subir</button>
</form>
El atributo accept permite filtrar tipos MIME en el selector de archivos, mejorando la experiencia del usuario.
Ejemplo completo con Bootstrap 5
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Subida de archivos</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div class="container py-5">
<h2 class="mb-4">Formulario de carga de documentos</h2>
<form action="/api/documents" method="post" enctype="multipart/form-data" class="row g-3">
<div class="col-md-6">
<label for="title" class="form-label">Título del documento</label>
<input type="text" name="title" id="title" class="form-control" required>
</div>
<div class="col-md-6">
<label for="doc" class="form-label">Archivo PDF</label>
<input type="file" name="doc" id="doc" class="form-control" accept="application/pdf" required>
</div>
<div class="col-12">
<button type="submit" class="btn btn-success">Enviar</button>
</div>
</form>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>
Este ejemplo muestra un formulario responsivo con dos columnas: datos textuales y el selector de archivo.
Comparativa de codificaciones de formularios
multipart/form-data
- Permite envío de binarios (archivos).
- Requiere
enctype="multipart/form-data". - Mayor tamaño de petición debido a los delimitadores.
- Soporte total en navegadores modernos.
application/x-www-form-urlencoded
- Codifica datos como
key=value&key2=value2. - No permite archivos binarios.
- Más eficiente para datos simples.
- Uso típico en APIs REST que esperan JSON.
text/plain
- Envía los campos como texto plano sin codificación.
- Rara vez usado; útil para depuración.
- No soporta archivos.
Buenas prácticas y optimización
- Limitar el tamaño máximo: Configura
client_max_body_size(NGINX) oLimitRequestBody(Apache) para evitar ataques de denegación de servicio. - Validar el tipo MIME del archivo: No confíes solo en la extensión; verifica
Content-Typey, de ser posible, inspecciona el encabezado del archivo. - Renombrar y almacenar de forma segura: Genera nombres únicos (UUID) y guarda fuera del directorio web para prevenir ejecución accidental.
- Usar HTTPS: Garantiza la confidencialidad e integridad de los archivos en tránsito.
- Chunked upload (carga por fragmentos): Para archivos >100 MB, considera APIs como Fetch con
Blob.slice()o soluciones como tus resumable.js.
Seguridad y mitigación de riesgos
Los uploads son un vector frecuente de vulnerabilidades. Aplica las siguientes defensas:
- Whitelist de extensiones: Permite solo los tipos estrictamente necesarios (ej.:
.jpg,.png,.pdf). - Escaneo antivirus: Integra herramientas como ClamAV o Microsoft Defender en el pipeline de procesamiento.
- Sandboxing: Ejecuta cualquier procesamiento de archivos (por ejemplo, generación de miniaturas) en contenedores aislados.
- Control de permisos de archivo: Almacena con
chmod 0640y propietario del proceso de la aplicación.
Depuración (troubleshooting)
Si la carga falla, revisa:
- Cabeceras HTTP:
Content-TypeyContent-Length(usar herramientas como httpie ocurl -v). - Logs del servidor web (NGINX
error.log, Apacheerror_log). - Configuración de límite de tamaño (
client_max_body_size,LimitRequestBody,upload_max_filesizeen PHP,maxRequestSizeen Spring). - Respuesta del backend: asegúrate de leer el cuerpo con la API adecuada (por ejemplo,
MultipartFileen Spring Boot o$_FILESen PHP).
Compatibilidad y rendimiento
Los navegadores modernos (Chrome, Edge, Firefox, Safari) implementan multipart/form-data de forma idéntica, por lo que la compatibilidad es prácticamente universal. En dispositivos móviles, el selector de archivos abre la galería o el gestor de archivos nativo.
En cuanto al rendimiento:
- El sobrecosto de los delimitadores es marginal (
- Para grandes volúmenes, habilita compresión
gzipen la respuesta y consideraTransfer-Encoding: chunkedpara la carga.
Conclusión
El tipo multipart/form-data es la solución robusta y segura para la subida de archivos en la web. Siguiendo las mejores prácticas de validación, seguridad y optimización, puedes ofrecer a tus usuarios una experiencia fluida y proteger tu infraestructura de ataques comunes.
Guía completa sobre formularios multipart/form-data para subir archivos en HTML