FormData en JavaScript
Domina la interfaz FormData en JavaScript, herramienta clave de la Web API para gestionar formularios asíncronos. Aprende a capturar archivos y texto para enviarlos mediante fetch bajo el estándar multipart/form-data, optimizando la comunicación entre cliente y servidor en el desarrollo web moderno.

Índice
1. ¿Qué es FormData en JavaScript?
FormData es una interfaz nativa de JavaScript que permite recopilar, almacenar y enviar datos de formularios HTML de manera sencilla.
Su función principal es emular el formato multipart/form-data, que es el estándar utilizado por el navegador para enviar datos a través de una petición HTTP (usualmente mediante fetch o XMLHttpRequest).
QUÉ ES multipart/form-data?
multipart/form-data es un tipo de contenido MIME (Multipurpose Internet Mail Extensions) que permite la transmisión de datos binarios y textuales de forma simultánea. Se caracteriza por utilizar una cadena de caracteres única llamada boundary para delimitar cada parte del cuerpo de la petición HTTP, garantizando la integridad de los archivos durante la transferencia.
¿Cuándo usarlo?
- Siempre que tu formulario incluya un
<input type="file">. - Cuando necesites enviar datos estructurados que incluyan archivos adjuntos.
- Cuando utilices el objeto FormData en JavaScript para peticiones con
fetch().
En otras palabras, FormData convierte automáticamente los campos de un formulario en un conjunto de pares clave–valor, donde:
- La clave corresponde al atributo
namede cada campo. - El valor corresponde al contenido ingresado por el usuario.
Por ejemplo, si un formulario tiene un campo llamado nombre y otro llamado email, FormData generará internamente una estructura similar a esta:
nombre = "Juan"
email = "[email protected]"Lenguaje del código: JavaScript (javascript)
1.2. Características clave de FormData
- Envío de archivos:
- A diferencia de un objeto JSON plano,
FormDatapermite adjuntar archivos (File) y blobs de forma nativa.
- A diferencia de un objeto JSON plano,
- Codificación automática:
- El navegador configura automáticamente el
Content-Typecorrecto, incluyendo el boundary necesario para que el servidor procese los datos.
- El navegador configura automáticamente el
- Interfaz iterable:
- Permite recorrer los datos mediante métodos como
keys(),values()yentries().
- Permite recorrer los datos mediante métodos como
1.2. ¿Para qué sirve FormData?
FormData sirve para trabajar con datos de formularios sin tener que leer manualmente cada campo del DOM.
Es especialmente útil para:
- Enviar formularios de registro o inicio de sesión.
- Enviar formularios de contacto.
- Subir archivos e imágenes.
- Enviar datos mediante peticiones AJAX o
fetch(). - Trabajar con formularios dinámicos generados desde JavaScript.
1.3. ¿Por qué FormData es importante en JavaScript?
Antes de FormData, era necesario acceder manualmente a cada campo del formulario:
const nombre = document.querySelector('#nombre').value;
const email = document.querySelector('#email').value;Lenguaje del código: JavaScript (javascript)
Con FormData, puedes obtener todos los datos de una sola vez:
const formulario = document.querySelector('form');
const datos = new FormData(formulario);Lenguaje del código: JavaScript (javascript)
Esto hace que el código sea:
- Más corto.
- Más fácil de mantener.
- Más escalable cuando el formulario tiene muchos campos.
- Más útil para proyectos reales donde los formularios cambian con frecuencia.
2. ¿Cómo usar FormData?
🚨OJO!! Existen dos formas principales de trabajar con FormData en JavaScript:
- Crear
FormDatadirectamente desde un formulario HTML. - Crear
FormDatamanualmente connew FormData()y luego agregar los datos uno por uno.
Ninguna de las dos está obsoleta. Ambas siguen siendo actuales y se usan dependiendo de la situación.
ACLARACIÓN! A continuación dejamos un resumen de estos dos casos y luego iremos profundizando.
2.1. FormData creado desde un formulario HTML
const formulario = document.querySelector('#miFormulario');
const formData = new FormData(formulario);Lenguaje del código: JavaScript (javascript)
Este enfoque toma automáticamente todos los campos del formulario que tengan atributo name.
Por ejemplo, si tienes este formulario HTML:
<form id="miFormulario">
<input type="text" name="nombre">
<input type="email" name="email">
</form>Lenguaje del código: HTML, XML (xml)
Y hacemos:
new FormData(formulario)Lenguaje del código: JavaScript (javascript)
Capturará automáticamente:
nombre → valor escrito
email → valor escrito
2.1.1. Cuándo conviene usarlo
Usa esta forma cuando:
- Ya tienes un
<form>en el HTML. - Quieres obtener todos los datos del formulario de una sola vez.
- El formulario tiene muchos campos.
- Quieres escribir menos código.
- Estás trabajando con formularios clásicos de login, registro, contacto, etc.
2.1.2. Ventajas
- Más rápido de implementar.
- Menos código.
- Muy útil si el formulario ya existe en el DOM.
- También captura automáticamente archivos, checkboxes, radios, etc.
2.1.3. Desventaja
- Captura todos los campos del formulario, incluso si no quieres enviar algunos.
- Tienes menos control sobre qué datos se agregan inicialmente.
2.2. FormData creado manualmente
const formData = new FormData();
formData.append('nombre', 'Juan');
formData.append('email', '[email protected]');Lenguaje del código: JavaScript (javascript)
Como vemos, no colocamos nada entre los paréntesis del constructor, esto nos permite que luego con .append() ir agregando los campos manualmente (profundizaremos)
Aquí tú decides exactamente qué campos agregar.
2.2.1. Cuándo conviene usarlo
Usa esta forma cuando:
- Los datos no vienen de un
<form>. - Estás construyendo datos dinámicamente desde JavaScript.
- Necesitas combinar información de varios lugares.
- Quieres agregar valores extra que no existen en el formulario.
- Necesitas modificar datos antes de enviarlos.
- Estás trabajando con componentes dinámicos o formularios creados desde el DOM.
Por ejemplo:
const formData = new FormData();
formData.append('nombre', inputNombre.value);
formData.append('token', 'abc123');
formData.append('fecha', new Date().toISOString());Lenguaje del código: JavaScript (javascript)
Aquí token y fecha no vienen del formulario HTML.
2.2.2. Ventajas
- Máximo control.
- Puedes agregar, eliminar o modificar cualquier dato.
- Ideal para aplicaciones más complejas.
2.2.3. Desventaja
- Requiere más código.
- Debes agregar cada campo manualmente.
2.3. Diferencia principal
| Diferencia principal | Desde formulario HTML | Manualmente |
|---|---|---|
| Captura de datos | Captura automáticamente todos los campos del formulario | Tú agregas los campos uno por uno |
| Requisitos | Requiere un <form> existente |
No necesita un formulario |
| Flexibilidad | Menos flexible | Más flexible |
| Cantidad de código | Menos código | Más código |
| Uso ideal | Ideal para formularios simples o medianos | Ideal para lógica dinámica o avanzada |
Lo más común en proyectos reales
En la práctica, muchas veces se usan las dos técnicas juntas.
Por ejemplo:
const formulario = document.querySelector('#registro');
const formData = new FormData(formulario);
// Agregar datos extra manualmente
formData.append('token', 'abc123');
formData.append('usuarioId', 15);Lenguaje del código: JavaScript (javascript)
Primero se toman automáticamente los datos del formulario y luego se agregan valores extra manualmente.
Este suele ser el enfoque más usado en proyectos modernos.
3. Crear FormData desde formulario HTML
Cuando hablamos de un objeto FormData creado desde un formulario HTML, nos referimos al proceso de automatizar la recolección de datos.
En lugar de escribir código para obtener el valor de cada campo uno por uno, le pides a JavaScript que «extraiga» toda la información de un elemento <form> existente de un solo golpe.
Aquí tienes el desglose de lo que esto significa técnicamente:
Al pasar un formulario como argumento al constructor:
new FormData(miFormulario)Lenguaje del código: JavaScript (javascript)
Lo que JavaScript realiza un escaneo del DOM. El objeto resultante es una representación en memoria de todos los controles de ese formulario.
Requisitos para que funcione:
Para que un campo sea incluido en el FormData, el formulario HTML debe cumplir dos reglas:
- Tener el atributo
name:- Sin este atributo, el campo es invisible para
FormData. El valor denameserá la clave y el contenido del input será el valor.
- Sin este atributo, el campo es invisible para
- No estar deshabilitado:
- Los campos con el atributo
disabledson ignorados automáticamente.
- Los campos con el atributo
3.1. La Sintaxis en Acción
Imagina que tienes este formulario en tu HTML:
HTML
<form id="registro">
<input type="text" name="usuario" value="Alex">
<input type="email" name="correo" value="[email protected]">
</form>Lenguaje del código: HTML, XML (xml)
Cuando ejecutas esto en tu script:
const formularioDoc = document.getElementById('registro');
const datos = new FormData(formularioDoc);Lenguaje del código: JavaScript (javascript)
Lo que significa es que la constante datos ahora es un objeto especial que ya contiene:
usuario-> «Alex»correo-> «[email protected]»
¿Por qué se dice que está «creada desde HTML»?
Se dice así porque el objeto depende totalmente del estado actual del formulario en la pantalla.
- Es una fotografía instantánea: Si el usuario escribe algo nuevo en el input y luego ejecutas el código,
FormDatacapturará los valores nuevos en ese preciso momento. - Soporte de archivos: Si el formulario tiene un
<input type="file">, el objetoFormDataincluirá automáticamente el archivo binario, algo que es muy difícil de hacer manualmente con otros métodos.
3.2. ¿Donde incluyo este FormData en la petición HTTP con Fetch?
Imagina que tienes un formulario de contacto. Aquí está el código necesario para procesarlo sin recargar la página:
// 1. Seleccionamos el formulario del DOM
const miFormulario = document.getElementById('mi-formulario-contacto');
// 2. Escuchamos el evento de envío
miFormulario.addEventListener('submit', async (event) => {
// Evitamos que la página se refresque
event.preventDefault();
// --- AQUÍ INCLUIMOS LA SINTAXIS DE FORMDATA ---
const datos = new FormData(miFormulario);
// ----------------------------------------------
try {
// 3. Enviamos los datos usando Fetch API
const respuesta = await fetch('https://api.tu-sitio.com/enviar', {
method: 'POST',
body: datos // <--- Aquí es donde se incluye el FormData
});
if (respuesta.ok) {
const resultado = await respuesta.json();
alert('¡Formulario enviado con éxito!');
}
} catch (error) {
console.error('Hubo un error en el envío:', error);
}
});
Lenguaje del código: JavaScript (javascript)
¿Dónde se incluye exactamente el FormData?
La sintaxis const datos = new FormData(miFormulario); debe incluirse dentro del objeto de configuración de Fetch, específicamente en la propiedad body.
Explicación detallada:
- La propiedad
body:- Es el «cuerpo» de tu mensaje HTTP. Cuando haces una petición
POST, necesitas enviar información al servidor. Al colocar el objetodatos(que es tuFormData) aquí, le estás diciendo a Fetch: «Toma todo lo que encontraste en el formulario HTML y ponlo dentro de esta petición».
- Es el «cuerpo» de tu mensaje HTTP. Cuando haces una petición
- ¿Por qué ahí?:
- Porque la Fetch API está diseñada para recibir diferentes tipos de cuerpos. Al detectar que el
bodyes una instancia deFormData, Fetch hace algo inteligente: configura automáticamente los encabezados (headers) por ti.
- Porque la Fetch API está diseñada para recibir diferentes tipos de cuerpos. Al detectar que el
⚠️Lo que NO debes hacer (Error común de principiantes):
Muchos desarrolladores intentan añadir manualmente esto: headers: { "Content-Type": "multipart/form-data" }
¡No lo hagas! Si incluyes esa línea manualmente, romperás el «boundary» (la frontera invisible que separa los campos) y el servidor no podrá leer los datos. Al poner body: datos, el navegador se encarga de todo el trabajo sucio.
4. FormData creado manualmente
FormData creado manualmente es una forma de construir y enviar datos desde JavaScript sin depender directamente de un formulario HTML completo (como en el punto anterior).
En lugar de crear el objeto a partir de un <form> existente, se crea una instancia vacía con new FormData() y luego se agregan los datos uno por uno mediante métodos como append() o set().
// Crear un FormData vacío
const datos = new FormData();
// Agregar datos manualmente con .append()
datos.append("nombre", "Manuel");
datos.append("email", "[email protected]");
datos.append("edad", 25);Lenguaje del código: JavaScript (javascript)
Este enfoque resulta especialmente útil cuando los datos no provienen únicamente de un formulario tradicional, sino también de elementos del DOM creados dinámicamente, valores calculados con JavaScript o información adicional generada durante la ejecución.
Como dijimos anteriormente, puede ser necesario para enviar:
- Valores de varios inputs obtenidos con
querySelector() - Archivos seleccionados por el usuario
- Un token de seguridad
- La fecha actual
- Datos agregados dinámicamente a la interfaz
La principal diferencia entre FormData manual y new FormData(formulario) es el nivel de control:
new FormData(formulario)toma automáticamente todos los campos de un<form>new FormData()permite decidir exactamente qué datos enviar y en qué momento
// Automático: toma todos los campos del formulario
const datosAutomaticos = new FormData(formulario);
// Manual: tú eliges cada dato
const datosManuales = new FormData();
datosManuales.append("nombre", "Manuel");Lenguaje del código: JavaScript (javascript)
4.1. Ejemplo usando FormData manualmente
ESCENARIO: El Dashboard Interactivo (Sin formularios)
Imagina que estás construyendo una aplicación de edición de fotos o un panel de control avanzado. En este diseño, no tienes un formulario tradicional con etiquetas <input>. En su lugar, el usuario:
- Arrastra una imagen a un sector de la pantalla.
- Elige una categoría haciendo clic en una tarjeta visual.
- El sistema añade automáticamente la ubicación GPS y la fecha actual.
Como estos datos están «dispersos» en la memoria de tu aplicación (variables, objetos o estados de React/Vue), no puedes simplemente capturar un formulario del DOM. Aquí es donde entra en juego el constructor manual de FormData.
CÓDIGO:
En este ejemplo, recolectamos datos de diferentes fuentes y los empaquetamos manualmente para enviarlos al servidor:
// Supongamos que estos datos vienen de diferentes partes de tu app
const imagenProcesada = obtenerImagenDesdeCanvas(); // Un Blob/File
const categoriaSeleccionada = "Edición_Pro";
const metadata = {
prioridad: "Alta",
timestamp: Date.now()
};
// 1. Usamos el constructor sin argumentos para crear un sobre vacío
const datosDeEnvio = new FormData();
// 2. Poblamos el objeto manualmente
datosDeEnvio.append('file', imagenProcesada, 'foto_editada.jpg');
datosDeEnvio.append('categoria', categoriaSeleccionada);
datosDeEnvio.append('info_extra', JSON.stringify(metadata));Lenguaje del código: JavaScript (javascript)
Hasta este punto, hemos visto cómo crear el contenedor vacío. Pero, antes de enviarlo, es fundamental entender el motor que permite llenar ese «sobre» con información: el método .append().
4.2. Cómo agregar datos dinámicamente con .append()
El método .append() es la función principal de la interfaz FormData para insertar nuevos pares de clave/valor. Su nombre (que significa «anexar») es muy preciso: cada vez que lo llamas, añades una nueva pieza de información al final del objeto.
4.2.1. La sintaxis de .append()
Este método puede recibir hasta tres argumentos, dependiendo de si estás enviando texto simple o un archivo complejo:
objetoFormData.append(nombre, valor, nombreDeArchivo);
nombre(Key):- El nombre del campo (equivalente al atributo
nameen HTML). Es la etiqueta que el servidor buscará para identificar el dato.
- El nombre del campo (equivalente al atributo
valor(Value):- El contenido. Puede ser un
string, unnumber, unBlobo un objetoFile.
- El contenido. Puede ser un
nombreDeArchivo(Opcional):- Solo se usa cuando el valor es un archivo o un
Blob. Permite decirle al servidor cómo debe llamarse ese archivo (ej:"perfil.jpg").
- Solo se usa cuando el valor es un archivo o un
4.2.2. ¿Por qué decimos que es «dinámico»?
A diferencia de un formulario estático donde los campos ya están definidos, con .append() puedes usar toda la potencia de JavaScript (bucles, condicionales y eventos) para decidir qué datos incluir en el último segundo.
4.2.3. Puntos clave para recordar
- No sobrescribe: Si usas
.append()dos veces con el mismo nombre de clave, el objeto guardará ambos valores. Esto es útil para enviar arreglos de datos. - Conversión automática: Si pasas un número o un booleano,
FormDatalo convertirá automáticamente a una cadena de texto (string) para cumplir con el protocolo HTTP. - Gestión de Archivos: Es el único método nativo que permite «inyectar» archivos binarios directamente en una petición sin tener que manipular manualmente el código binario.
BIEN… una vez que dominas cómo agregar datos, el siguiente nivel de control es saber cómo actualizarlos o corregirlos antes de que salgan disparados hacia el servidor.
Aquí tienes la continuación lógica centrada en el método .set().
4.3. ¿Cómo corregir o actualizar datos con .set()?
En nuestro ejemplo del Dashboard, vimos que usamos .append() para ir llenando el «sobre» con información. Pero, ¿qué pasa si el usuario cambia de opinión en el último segundo? ¿O si el sistema genera un nuevo timestamp más preciso justo antes de enviar?
Aquí es donde entra el método .set(). A diferencia de .append(), que simplemente añade más cosas al final, .set() actúa como un comando de «Reemplazo Total».
MODIFICAMOS EL EJEMPLO ANTERIOR Y AÑADIMOS .set()
Escenario de actualización: Cambiando la categoría
Imagina que inicialmente agregamos la categoría «Edición_Pro», pero justo antes de que el código ejecute el fetch, detectamos que el usuario tiene una cuenta básica. Necesitamos actualizar ese valor sin crear un duplicado.
// 1. Ya tenemos nuestro objeto con datos previos
const datosDeEnvio = new FormData();
datosDeEnvio.append('categoria', 'Edición_Pro');
datosDeEnvio.append('prioridad', 'Alta');
// 2. Supongamos que ocurre un cambio de lógica
const esUsuarioPremium = false;
if (!esUsuarioPremium) {
// Usamos .set() para reemplazar el valor anterior
datosDeEnvio.set('categoria', 'Edición_Basica');
// También podemos actualizar el timestamp al momento exacto del envío
datosDeEnvio.set('timestamp', Date.now());
}
// 3. Resultado: 'categoria' ahora es "Edición_Basica" y no existe rastro de "Edición_Pro"Lenguaje del código: JavaScript (javascript)
4.3.1. ¿Qué hace exactamente .set()?
En lugar de simplemente añadir datos, .set() realiza una operación de «Buscar, Borrar y Escribir». Su comportamiento en tu código se divide en dos acciones:
- Sobrescribe valores existentes (Overwrite)
- Cuando ejecutas
datosDeEnvio.set('categoria', 'Edición_Basica'), el navegador busca si ya existe la clave'categoria'.- Lo que hace: Elimina por completo el valor anterior (
'Edición_Pro') y coloca el nuevo en su lugar. - El resultado: Evita que el servidor reciba dos categorías distintas para el mismo archivo, lo cual causaría errores de lógica en el backend.
- Lo que hace: Elimina por completo el valor anterior (
- Cuando ejecutas
- Crea claves nuevas si no existen
- En la línea
datosDeEnvio.set('timestamp', Date.now()), la clave'timestamp'aún no existía en el objeto.- Lo que hace: Al no encontrar la clave,
.set()se comporta exactamente igual que.append()y crea la entrada desde cero.
- Lo que hace: Al no encontrar la clave,
- En la línea
4.3.2. Diferencia clave: .append() vs .set()
.append(clave, valor): Dice: «Añade esto». Si la clave ya existe, ahora tendrás dos valores para esa misma clave. Es como meter dos papeles con el mismo nombre en un sobre..set(clave, valor): Dice: «Asegúrate de que este sea el único valor». Si la clave ya existe, borra todo lo anterior y lo reemplaza por el nuevo. Si la clave no existe, la crea (exactamente como lo haría append).
4.3.3. ¿Por qué usar .set() mejora tu código?
- Integridad de Datos:
- Evitas enviar «basura» o valores contradictorios al backend. Si tu servidor espera un solo string para la categoría y le envías un arreglo de dos (porque usaste append dos veces), podrías provocar un error 500.
- Optimización de Memoria:
- Al reemplazar en lugar de acumular, mantienes el objeto
FormDatalo más ligero posible antes de la transmisión.
- Al reemplazar en lugar de acumular, mantienes el objeto
- Lógica Dinámica:
- Es ideal para aplicaciones con muchos pasos o wizards de carga, donde el usuario puede retroceder y cambiar sus opciones.
4.4. Agregar datos obtenidos desde inputs con .value
Siguiendo con nuestro ejemplo del Dashboard Interactivo, ya logramos gestionar categorías y prioridades de forma interna. Pero, ¿qué pasa si queremos que el usuario le ponga un título personalizado a su proyecto o una nota aclaratoria?
En este caso, la información no vive en una variable oculta, sino que reside dentro de un elemento visual del DOM (un campo de texto). Para extraer esa información y meterla en nuestro FormData, utilizaremos la propiedad .value.
Preparando el HTML
Para que esto funcione, necesitamos un campo donde el usuario pueda escribir. En nuestro Dashboard, añadiremos un simple input de texto:
<div class="user-input-section">
<label for="input-titulo">Título del Proyecto:</label>
<input type="text" id="input-titulo" placeholder="Ej: Mi viaje a la montaña">
</div>Lenguaje del código: JavaScript (javascript)
Captura y envío en JavaScript
Ahora, dentro de nuestra lógica de envío, capturamos ese elemento y extraemos su contenido justo antes de preparar el FormData.
// 1. Localizamos el input en el DOM
const inputTitulo = document.querySelector('#input-titulo');
// 2. Creamos nuestro objeto FormData manual
const datosDeEnvio = new FormData();
// 3. Extraemos el texto usando .value y lo añadimos
// 'titulo_proyecto' es la clave que recibirá el servidor
datosDeEnvio.append('titulo_proyecto', inputTitulo.value);
// Podemos seguir sumando datos como veníamos haciendo
datosDeEnvio.append('categoria', 'Edición_Pro');Lenguaje del código: JavaScript (javascript)
¿Qué es exactamente lo que hace .value en este ejemplo?
- Captura del Estado Actual:
- La propiedad
.valueactúa como un «escáner» que lee exactamente lo que hay escrito dentro del cuadro de texto en el preciso instante en que se ejecuta la línea de código.
- La propiedad
- Conversión a String:
- Todo lo que se obtiene a través de
.value(aunque el usuario escriba números) se recupera como una cadena de texto (string). Esto es perfecto porque es el formato que los servidores web esperan para los campos de texto.
- Todo lo que se obtiene a través de
- Puente entre el Usuario y el Objeto:
- Sin
.value, el objetoinputTitulosería solo una referencia técnica a una etiqueta HTML. Al usar.value, transformamos esa etiqueta visual en un dato real que puede viajar a través de internet.
- Sin
NOTA!!! Mientras que los archivos requieren métodos más complejos, para cualquier dato textual (nombres, descripciones, correos o fechas escritas), la propiedad .value combinada con document.querySelector() es la forma estándar y más eficiente de poblar un objeto FormData de manera manual.
4.5. Agregar archivos desde un input tipo file (propiedad .files)
En nuestro Dashboard Interactivo, ya capturamos el título del proyecto usando .value. Pero el corazón de la aplicación es la imagen que el usuario desea procesar. Para capturar este archivo real y «meterlo» en nuestro FormData, no podemos usar la propiedad anterior; debemos recurrir a la propiedad .files.
El HTML: El selector de archivos
Para que el usuario pueda buscar en su computadora, necesitamos un input con el tipo específico file.
<div class="upload-area">
<label for="input-archivo">Sube tu fotografía:</label>
<input type="file" id="input-archivo" accept="image/*">
</div>Lenguaje del código: JavaScript (javascript)
JavaScript: Accediendo al recurso binario
A diferencia de los campos de texto, los archivos se almacenan en una lista especial. Así es como los extraemos y los añadimos al envío:
// 1. Seleccionamos el input de tipo archivo
const inputArchivo = document.querySelector('#input-archivo');
// 2. Creamos (o continuamos) nuestro objeto FormData
const datosDeEnvio = new FormData();
// 3. Accedemos a la propiedad .files
// Esta propiedad devuelve una lista. El primer archivo está en el índice [0]
if (inputArchivo.files.length > 0) {
const elArchivoReal = inputArchivo.files[0];
// 4. Lo añadimos al FormData
// 'imagen_usuario' es la clave; el segundo parámetro es el archivo real
datosDeEnvio.append('imagen_usuario', elArchivoReal);
}
// Seguimos sumando el título que vimos antes
datosDeEnvio.append('titulo_proyecto', document.querySelector('#input-titulo').value);Lenguaje del código: JavaScript (javascript)
¿Por qué usamos .files y no .value para los archivos?
.valuees una mentira (Fakepath):- Si intentas usar
.valueen un input de archivo, obtendrás algo como"C:\fakepath\foto.jpg". Esto es solo un texto de seguridad; no contiene los datos de la imagen. El servidor recibiría un nombre, pero no el archivo.
- Si intentas usar
.fileses el contenedor real:- Esta propiedad devuelve un objeto llamado
FileList. Es una lista que contiene objetos de tipoFile, los cuales incluyen los metadatos (nombre, tamaño, tipo) y, lo más importante, el contenido binario del archivo.
- Esta propiedad devuelve un objeto llamado
- Preparación para la red:
- Al pasar el objeto obtenido de
.files[0]al método.append(), el navegador se encarga de fragmentar el archivo en bits para que pueda viajar por internet sin corromperse.
- Al pasar el objeto obtenido de
NOTA!!! En JavaScript, el acceso a archivos locales se realiza exclusivamente a través de la interfaz FileList. Al utilizar .files[0], extraemos el recurso binario necesario para que el objeto FormData construya una petición multipart/form-data válida, permitiendo la carga de imágenes, documentos o videos de forma eficiente.
BIEN… ahora es el momento en que el paquete de información que hemos construido sale de nuestra aplicación hacia el servidor. Para esto, la herramienta estándar y más moderna es la Fetch API.
4.6. Cómo enviar FormData con la Fetch API
Una vez que nuestro objeto datosDeEnvio está lleno con el título, la categoría y la imagen, debemos realizar la petición HTTP, en este caso lo haremos usando Fetch API.
La sintaxis es sorprendentemente limpia, pero requiere entender un par de reglas fundamentales para que el servidor pueda interpretar los datos.
EL CÓDIGO DE ENVÍO:
Continuando con nuestro ejemplo del Dashboard, así es como ejecutaríamos el envío final:
// 1. Ya tenemos nuestro objeto datosDeEnvio preparado de los pasos anteriores
// 2. Ejecutamos la petición asíncrona
fetch('https://api.tu-sitio.com/upload', {
method: 'POST', // Especificamos que vamos a ENVIAR datos
body: datosDeEnvio // Pasamos el objeto FormData directamente aquí
})
.then(respuesta => {
if (respuesta.ok) {
return respuesta.json(); // Convertimos la respuesta del servidor a JSON
}
throw new Error('Error en el servidor');
})
.then(data => {
console.log('Servidor recibió los datos:', data);
alert('¡Publicación creada con éxito!');
})
.catch(error => {
console.error('Hubo un fallo en la comunicación:', error);
});
Lenguaje del código: JavaScript (javascript)
EXPLICACIÓN: ¿Por qué lo hacemos así?
- El uso del método
POST- Al usar
method: 'POST', abrimos un canal de comunicación seguro y capaz de transportar el cuerpo (body) completo de nuestroFormData, sin importar su tamaño.
- Al usar
- El objeto
bodycomo destino- Asignamos
datosDeEnviodirectamente a la propiedadbody. Al hacer esto, le estamos entregando a la Fetch API el «sobre» que preparamos manualmente. Fetch sabe internamente cómo leer este objeto y cómo transmitirlo bit a bit hacia la red.
- Asignamos
- La «Magia» de los encabezados automáticos (Headers)
- Esta es la parte más importante que debes explicar en tu artículo. Notarás que en el código no estamos definiendo el
Content-Type.- Por qué es correcto: Cuando el navegador ve que el
bodyes una instancia deFormData, él mismo configura el encabezado comomultipart/form-data. - El detalle técnico: Además, el navegador genera automáticamente una cadena única llamada boundary (frontera). Si intentaras escribir el header a mano, podrías olvidar el boundary y el servidor no sabría separar el título de la imagen, rompiendo la petición.
- Por qué es correcto: Cuando el navegador ve que el
- Esta es la parte más importante que debes explicar en tu artículo. Notarás que en el código no estamos definiendo el
Resumen de la integración de FormData manual y Fetch:
La forma correcta de transmitir datos complejos en JavaScript es mediante el uso de la Fetch API con el método POST. Al asignar una instancia de FormData a la propiedad body, el navegador gestiona de forma nativa la serialización de datos y la configuración de los encabezados multipart, permitiendo una transferencia eficiente de recursos multimedia y campos textuales en una sola operación asíncrona.
5. Diferencia entre FormData y JSON
FormData y un objeto JavaScript convertido a JSON sirven para enviar información al servidor, pero no funcionan de la misma manera ni se usan en las mismas situaciones.
La principal diferencia es que FormData está pensado para trabajar con formularios HTML y archivos, mientras que JSON está pensado para enviar estructuras de datos en texto.
| FormData | Objeto convertido a JSON |
|---|---|
| Se usa principalmente con formularios HTML | Se usa principalmente con datos generados desde JavaScript |
| Permite enviar archivos, imágenes y documentos | No puede enviar archivos directamente |
Utiliza el formato multipart/form-data |
Utiliza el formato application/json |
Se crea con new FormData() |
Se crea con un objeto JavaScript y JSON.stringify() |
| Toma fácilmente los datos desde el DOM | Requiere construir el objeto manualmente |
| Muy útil para formularios y subida de archivos | Muy útil para APIs REST y datos estructurados |
Por ejemplo, si tienes un formulario de registro con nombre, email y una foto de perfil, FormData es la mejor opción:
const formData = new FormData();
formData.append('nombre', 'Juan');
formData.append('email', '[email protected]');
formData.append('foto', archivoInput.files[0]);Lenguaje del código: JavaScript (javascript)
Esto funciona porque FormData puede incluir texto y archivos dentro de la misma petición.
En cambio, si solo necesitas enviar datos de texto a una API, suele ser más común usar JSON:
const datos = {
nombre: 'Juan',
email: '[email protected]'
};fetch('/api/usuarios', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(datos)
});Lenguaje del código: JavaScript (javascript)
La gran ventaja de JSON es que es más fácil de leer, más ligero y más utilizado en APIs modernas. Sin embargo, JSON no puede enviar archivos directamente. Si intentaras agregar un archivo dentro del objeto, el resultado no sería el esperado.
Por esa razón, la regla práctica suele ser esta:
- Usa
FormDatacuando el formulario incluya archivos, imágenes o datos obtenidos directamente desde el DOM. - Usa JSON cuando solo necesites enviar texto, números, booleanos u objetos a una API.
También existe una diferencia importante en los headers. Cuando usas JSON, debes indicar manualmente el tipo de contenido:
headers: {
'Content-Type': 'application/json'
}Lenguaje del código: JavaScript (javascript)
Pero cuando usas FormData, no debes establecer ese header manualmente, porque el navegador lo genera automáticamente:
fetch('/api', {
method: 'POST',
body: formData
});Lenguaje del código: JavaScript (javascript)
Si configuras Content-Type manualmente al usar FormData, es posible que la petición falle.
En proyectos reales, ambos enfoques conviven constantemente:
- Formularios de login, registro o subida de archivos →
FormData - APIs REST, paneles administrativos o aplicaciones SPA → JSON
En resumen, FormData es la mejor opción para formularios y archivos, mientras que JSON es la mejor opción para enviar datos estructurados entre JavaScript y una API.
6. Ejemplo real usando FormData
ESCENARIO: El Formulario de Postulación Laboral
Imagina que tienes una página donde los usuarios se postulan para un empleo. El formulario pide su nombre y su CV (archivo). Sin embargo, tú, como desarrollador, quieres enviar información extra que el usuario no escribe, como la fuente de donde vino (ej: LinkedIn) y la fecha exacta del clic.
1. El código HTML
Este es un formulario estándar. Lo importante aquí es que los inputs tengan el atributo name.
<form id="form-empleo">
<label>Nombre Completo:</label>
<input type="text" name="nombre_aspirante" id="nombre" required>
<label>Sube tu CV (PDF):</label>
<input type="file" id="cv-archivo" accept=".pdf">
<button type="submit">Enviar Postulación</button>
</form>
<p id="fuente-trafico" style="display:none">LinkedIn_Campaign_2026</p>Lenguaje del código: HTML, XML (xml)
2. El código JavaScript con FormData:
Aquí unificamos la captura automática, la captura manual con .value y .files, y el envío final con fetch.
const formulario = document.querySelector('#form-empleo');
formulario.addEventListener('submit', async (e) => {
e.preventDefault(); // Evitamos que la página se recargue
// PASO 1: Captura automática de lo que ya está en el HTML
const datos = new FormData(formulario);
// PASO 2: Captura manual de un archivo usando .files
const inputArchivo = document.querySelector('#cv-archivo');
if (inputArchivo.files.length > 0) {
datos.append('documento_cv', inputArchivo.files[0]);
}
// PASO 3: Captura manual de texto usando .value
// Supongamos que queremos capturar la fuente de un elemento oculto
const fuenteTrafico = document.querySelector('#fuente-trafico').textContent;
datos.append('origen_postulacion', fuenteTrafico);
// PASO 4: Agregar datos dinámicos generados por el sistema
datos.append('fecha_envio', new Date().toISOString());
// PASO 5: Envío final con Fetch API
try {
const respuesta = await fetch('https://api.apinem.com/v1/postular', {
method: 'POST',
body: datos // El navegador configura el Content-Type automáticamente
});
const resultado = await respuesta.json();
console.log("Éxito:", resultado);
} catch (error) {
console.error("Error al enviar:", error);
}
});
Lenguaje del código: JavaScript (javascript)
Explicación:
- Eficiencia con
new FormData(formulario):- Al iniciar el objeto pasando el formulario como argumento, ahorramos líneas de código. Automáticamente ya tenemos el nombre del aspirante porque el
inputtienename="nombre_aspirante".
- Al iniciar el objeto pasando el formulario como argumento, ahorramos líneas de código. Automáticamente ya tenemos el nombre del aspirante porque el
- Precisión con
.files[0]:- Al usar esta propiedad, nos aseguramos de que el servidor reciba el archivo PDF real y no solo el nombre del archivo. Esto soluciona el problema de enviar documentos adjuntos de forma asíncrona.
- Personalización con
.append()y.value:- Aquí es donde el desarrollador toma el control. Al capturar valores manualmente (como la fuente de tráfico o la fecha), estamos enriqueciendo la petición. El servidor recibirá un paquete completo con datos del usuario + datos técnicos del sistema.
¿Por qué usamos Fetch aquí?
Usamos fetch porque permite que el usuario envíe su postulación sin que la página parpadee o se recargue. Esto mejora drásticamente la experiencia de usuario (UX), haciendo que la aplicación se sienta moderna y fluida.
Resumen: El uso combinado del constructor FormData y la Fetch API representa el estándar moderno para la gestión de formularios asíncronos. Esta técnica permite la coexistencia de datos recolectados automáticamente del DOM con metadatos inyectados programáticamente, garantizando una transferencia de datos segura, tipada (multipart) y eficiente bajo protocolos HTTP contemporáneos.
7. Resumen de FormData en JavaScript
La interfaz FormData es una herramienta esencial del ecosistema Web API que permite construir, manipular y enviar conjuntos de datos (pares clave/valor) a través de peticiones HTTP. Es el estándar moderno para manejar información que combina texto y archivos binarios sin necesidad de recargar la página.
7.1. Métodos de Instanciación
- Desde HTML:
const datos = new FormData(miFormulario);- Función: Captura automáticamente todos los campos con atributo
namey sus valores actuales del DOM.
- Función: Captura automáticamente todos los campos con atributo
- Creación Manual:
const datos = new FormData();- Función: Crea un objeto vacío para ser poblado dinámicamente mediante lógica de programación.
7.2. Manipulación de Datos: .append() vs .set()
El control de la información se basa en dos métodos principales que definen cómo se estructura el «paquete» de datos:
| Método | Acción Principal | Comportamiento si la clave ya existe |
|---|---|---|
.append(k, v) |
Anexar | Añade el nuevo valor al final (permite múltiples valores por clave). |
.set(k, v) |
Reemplazar | Elimina todos los valores previos y establece solo el nuevo (garantiza unicidad). |
7.3. Captura de Datos desde el DOM
Para alimentar un objeto FormData manualmente desde elementos del navegador, debemos distinguir el tipo de dato:
- Inputs de Texto/Selección:
- Se accede mediante la propiedad
.value(devuelve unstring).
- Se accede mediante la propiedad
- Inputs de Archivos:
- Se accede mediante la propiedad
.files(devuelve un objetoFileListque contiene el recurso binario real).
- Se accede mediante la propiedad
7.4. Integración con Fetch API
FormData y fetch trabajan en perfecta sincronía bajo las siguientes reglas técnicas:
- Método POST:
- Es obligatorio usar
method: 'POST'ya que los datos viajan en el cuerpo (body) de la petición.
- Es obligatorio usar
- Omisión de Headers:
- No se debe definir manualmente el
Content-Type. El navegador detecta el objetoFormDatay configura automáticamente el encabezado comomultipart/form-data, incluyendo el boundary necesario para la integridad de los archivos.
- No se debe definir manualmente el
- Asincronía:
- El proceso no bloquea la interfaz de usuario, permitiendo gestionar la respuesta del servidor (éxito o error) mediante promesas (
.then()oasync/await).
- El proceso no bloquea la interfaz de usuario, permitiendo gestionar la respuesta del servidor (éxito o error) mediante promesas (
NOTA!! FormData simplifica la serialización de datos complejos. Al delegar la codificación al navegador, se reduce la probabilidad de errores en la transmisión de archivos binarios y se asegura la compatibilidad con los estándares de red modernos, siendo preferible sobre JSON cuando la petición incluye objetos File o Blob.
