Español

Herramientas de desarrollo · Codificador y decodificador Base64

Base64 en JSON API: por qué se codifican los campos binarios y cuánto le cuesta

· Por qué es importante

base64 codificación

Un campo JSON que contiene una cadena Base64 larga que representa datos binarios codificados para el transporte.
Ilustración de vector original de ToolAcre

JSON no tiene tipo de byte, por lo que los datos binarios suelen estar codificados en Base64 en una cadena. Esta publicación explica por qué existe esa convención, cuánto cuesta en tamaño y CPU, y cuándo es mejor utilizar un punto final binario separado.

El campo PDF que dominó la respuesta: una carga útil API concreta donde un blob Base64 pesaba más que todo lo demás.

Una respuesta API contiene un objeto grande con un único campo que domina el tamaño de la carga útil. La respuesta es JSON, por lo que cada valor es una cadena o un número. La mayoría de los campos son pequeños: ID de usuario, marcas de tiempo, códigos de estado. Un campo contiene imageData o fileContents y es una cadena Base64 de 40 kilobytes. La respuesta completa es 50 kilobytes. Ese único campo representa el 80 por ciento de la transferencia, lo que parece un desperdicio porque el servidor lo envió originalmente como bytes y el cliente eventualmente necesita bytes nuevamente.

Base64 resolvió este problema: JSON no tiene un tipo de byte nativo, por lo que los datos binarios deben estar envueltos en una cadena. Base64 convierte bytes arbitrarios en caracteres ASCII que están seguros en JSON. Tanto el cliente como el servidor deben codificar al enviar y decodificar al recibir, lo que agrega una sobrecarga de CPU. La carga útil resultante es aproximadamente un tercio más grande que los bytes sin procesar. Esta publicación explica por qué existe esa convención, cuánto cuesta en la práctica y cuándo vale la pena romper la restricción JSON con un punto final binario separado.

Por qué JSON no puede transportar bytes sin formato: las cadenas deben ser texto Unicode válido, por lo que los bytes arbitrarios necesitan un contenedor de texto

JSON es un formato de texto donde todos los valores deben ser texto Unicode válido. La especificación define cadenas, números, valores booleanos y nulos. No tiene una matriz de bytes ni un tipo de búfer. Si una API necesita devolver datos binarios como una imagen, una firma criptográfica o la carga de un archivo, no puede colocar los bytes sin procesar en el objeto JSON directamente. Los bytes pueden contener caracteres que los analizadores JSON interpretan como marcadores estructurales. Un byte nulo en medio de un blob binario podría terminar una cadena antes de tiempo o interrumpir el analizador.

La solución común es codificar los datos binarios como Base64, produciendo una cadena de caracteres ASCII que los analizadores JSON tratan como texto sin formato. Luego, el cliente receptor decodifica el Base64 en bytes y los utiliza. Este paso de codificación ocurre a nivel de API, oculto para la mayoría de los desarrolladores, pero es un costo real que se acumula cuando las API devuelven muchos campos binarios. Los costos de Base64 en JSON se acumulan a lo largo del ciclo de solicitud-respuesta. La penalización de tamaño es la primera: la salida Base64 es aproximadamente un 33 por ciento mayor que su entrada debido a la sobrecarga de codificación.

Los costos: un tercio más de bytes, tiempo de decodificación y copias de memoria, donde cada costo aparece en un cliente típico

Un archivo de vídeo de 30 megabytes se convierte en 40 megabytes cuando está codificado en Base64. Descargar 40 en lugar de 30 megabytes cuesta ancho de banda y batería en dispositivos móviles, y tiempo para los usuarios con conexiones lentas. El segundo costo es el tiempo de CPU. El servidor debe codificar los datos binarios como Base64 antes de poder convertirlos en JSON. El cliente debe analizar el JSON y luego decodificar cada campo Base64 en bytes. Para una respuesta con múltiples campos binarios o un cliente que procesa miles de respuestas, ese tiempo de CPU se acumula.

En dispositivos con restricciones, como teléfonos, las operaciones de cadenas de JavaScript y el TextDecoder utilizado para la decodificación Base64 consumen batería y ralentizan la aplicación. El tercer costo es la memoria: el analizador JSON crea un objeto de cadena para el campo Base64, luego la decodificación crea otra copia como Uint8Array. Se crean instancias de un campo grande dos veces en la memoria antes de que la aplicación pueda usarlo. Un ejemplo trabajado aclara el costo. Supongamos que un punto final de API devuelve datos de perfil de usuario, incluida una imagen de avatar de 100 kilobytes. El servidor lee la imagen del disco como bytes, la codifica en Base64 y la incluye en la respuesta JSON.

Ejemplo resuelto: inspeccionar un campo Base64 a partir de una respuesta API: decodificarlo en el navegador para confirmar lo que realmente envió el servidor

La respuesta JSON ahora tiene aproximadamente 135 kilobytes (33% de sobrecarga más los otros campos). El cliente descarga 135 kilobytes en lugar de 100. En el navegador, el analizador JSON crea un objeto de cadena JavaScript para los datos Base64.

Cuando la aplicación necesita la imagen, llama al decodificador Base64, que crea un Uint8Array de los 100 kilobytes originales. Durante los pocos milisegundos de decodificación, ambos objetos existen en la memoria. Si la página muestra diez perfiles con avatares, el coste se multiplica. La alternativa es que la API devuelva la respuesta JSON con una URL separada para cada recurso de avatar, permitiendo que el navegador maneje las descargas de imágenes con su almacenamiento en caché nativo, renderizado progresivo y administración de memoria.

Alternativas: URL de descarga separadas y de varias partes y puntos finales binarios sin formato: las ventajas y desventajas de cada uno

La compensación entre incluir binario en JSON y recuperarlo por separado depende del propósito de la API y los patrones de uso. Para una página de resultados de búsqueda que devuelve cientos de pequeñas imágenes en miniatura, obtener cada una como una solicitud separada anula la agrupación y el almacenamiento en caché de conexiones HTTP. Incluirlos como Base64 en la respuesta JSON podría ser más rápido. Para una página de perfil detallada que solicita una o dos imágenes de alta resolución, las descargas por separado son claramente mejores. La documentación de la API debe indicar el tamaño máximo de los campos Base64 y cuándo los clientes deben esperar puntos finales separados.

Si un campo excede regularmente uno o dos kilobytes, la estrategia Base64 en línea es una señal de que es necesario reconsiderar el diseño de la API. Existen alternativas a Base64 en JSON, pero cada una tiene sus ventajas y desventajas. Las respuestas MIME de varias partes separan el binario y el texto, por lo que la sección binaria se envía como bytes sin formato y solo la sección de texto es JSON. Esto requiere que el cliente analice un mensaje de varias partes en lugar de simplemente llamar a JSON.parse, lo que agrega complejidad. Una URL de descarga separada en la respuesta JSON indica al cliente que busque el recurso binario por separado.

Convenciones que vale la pena indicar en sus documentos API: alfabeto estándar versus base64url, relleno y tamaños máximos

Esto funciona bien cuando el recurso binario es grande o se accede a él con menos frecuencia que los metadatos. Un punto final binario sin formato que devuelve solo bytes y abandona JSON por completo es el enfoque más simple, pero elimina la estructura que proporciona JSON. Algunas API devuelven datos binarios comprimidos y los codifican en Base64, lo que reduce la penalización de tamaño pero agrega una sobrecarga de descompresión. La elección depende del uso esperado: los campos pequeños están bien en línea, los campos grandes pertenecen a recursos separados y vale la pena mantener los datos estructurados en JSON incluso con el costo de Base64.

Las convenciones son importantes para la interoperabilidad. Las API que codifican datos binarios en Base64 deben documentarlos claramente e indicar si el alfabeto es estándar o seguro para URL. Base64 estándar usa + y /, que son seguros en cadenas JSON pero no en URL. Base64 seguro para URL los reemplaza con - y _, que es apropiado para datos: URI pero que se escaparon innecesariamente en JSON. La documentación debe especificar si se incluye u omite el relleno, porque ambos son Base64 válidos, pero un cliente que espera relleno y recibe datos sin relleno fallará silenciosamente o producirá basura.

Lo que esto no cubre: protobuf, CBOR y otros formatos de serialización binaria

Para campos muy grandes o que se actualizan con frecuencia, documentar un punto final binario separado es esencial para que los clientes no intenten recuperar kilobytes de datos innecesarios. Depurar API con campos Base64 es sencillo con la herramienta adecuada. El codificador y decodificador Base64 le permite decodificar cualquier campo localmente en el navegador, sin almacenarlo ni enviarlo a ningún lado. Copie un campo Base64 de una respuesta JSON, péguelo en el decodificador y presione Decodificar. Para datos similares a texto (JSON dentro de Base64, por ejemplo), la salida decodificada aparece instantáneamente.

Para datos binarios como imágenes, la vista hexadecimal muestra los bytes. Esto ayuda a confirmar que el servidor envió lo que esperaba y que el decodificador de su cliente está funcionando correctamente. Si un campo se decodifica con datos inesperados, el problema está en la codificación del servidor o en cómo se copia el campo. Si se decodifica en un blob parcial, es posible que el campo se haya truncado o que la longitud de Base64 sea incorrecta. La decodificación local acelera la depuración en comparación con escribir el campo en un archivo y abrir herramientas externas.

Conclusión: Base64 en JSON es un compromiso, así que documentelo: cómo el codificador y descodificador Base64 le ayuda a inspeccionar y verificar los campos codificados localmente

El enfoque práctico de Base64 en las API es el conocimiento en lugar de la evitación. Base64 es la forma estándar de transportar datos binarios en JSON y funciona. Comprenda que cada campo Base64 cuesta un tercio más en tamaño y unos pocos milisegundos de tiempo de CPU por ciclo de solicitud-respuesta. Para pequeños metadatos críticos, como tokens de autenticación (donde el JWT en sí está codificado en Base64), el costo es insignificante. Para archivos adjuntos grandes, pregunte si el binario debe viajar en la misma respuesta o como un recurso separado.

Documente el esquema de codificación y los tamaños máximos en su especificación API. Al inspeccionar las respuestas, utilice el codificador y decodificador Base64 para verificar que los campos se decodifiquen correctamente y comprender lo que realmente envió el servidor. Esa disciplina mantiene visible la compensación y la decisión es deliberada y no accidental.