Back

Convertir Imágenes a Base64 con Canvas

Convertir Imágenes a Base64 con Canvas

Cuando necesitas exportar datos de imagen desde el navegador—ya sea para vistas previas del lado del cliente, incrustar gráficos pequeños o procesar imágenes antes de subirlas—la API Canvas de HTML es la herramienta más directa disponible. Este artículo explica cómo funciona la conversión de canvas a Base64 en JavaScript, cuándo usarla y dónde están los errores más comunes.

Puntos Clave

  • El método toDataURL() del canvas devuelve un URI de datos completo (con prefijo MIME), no una cadena Base64 sin procesar. Elimina el prefijo cuando solo necesites los datos codificados.
  • Prefiere toBlob() sobre toDataURL() para cargas de archivos e imágenes grandes, ya que es asíncrono y más eficiente en memoria.
  • PNG es el único formato de salida con soporte universal. El soporte para JPEG y WebP varía según el navegador.
  • Las imágenes de origen cruzado contaminarán el canvas a menos que el servidor envíe los encabezados CORS apropiados y establezcas crossOrigin = 'anonymous' antes de asignar el src de la imagen.

El Flujo de Trabajo Básico

Convertir una imagen a Base64 usando canvas sigue tres pasos: cargar la imagen, dibujarla en un elemento canvas y luego exportar los datos del canvas como una cadena codificada.

const canvas = document.getElementById('myCanvas');
const ctx = canvas.getContext('2d');
const image = new Image();

image.onload = function () {
  canvas.width = image.width;
  canvas.height = image.height;
  ctx.drawImage(image, 0, 0);

  const dataURL = canvas.toDataURL('image/png');
  console.log(dataURL); // "data:image/png;base64,iVBORw0KGg..."
};

image.src = '/path/to/local-image.jpg';

Esta es la base de la codificación de imágenes basada en canvas. El método toDataURL() devuelve un URI de datos completo, no una cadena Base64 sin procesar.

Data URL vs. Cadena Base64 Sin Procesar

Entender la diferencia es importante. canvas.toDataURL() devuelve una cadena en este formato:

data:image/png;base64,iVBORw0KGg...

Incluye un prefijo de tipo MIME, la etiqueta de codificación y luego los datos Base64 reales. Si solo necesitas la porción Base64 sin procesar—por ejemplo, para enviarla como JSON a un servidor—elimina el prefijo:

const base64String = canvas.toDataURL('image/jpeg').split(',')[1];

Esto divide en la primera coma y toma todo lo que viene después. El prefijo se descarta y permanecen los datos codificados sin procesar.

toDataURL vs. toBlob: ¿Cuál Deberías Usar?

Esta es la decisión que la mayoría de los artículos omiten. Aquí hay una comparación directa:

CaracterísticatoDataURL()toBlob()
Tipo de retornoString (síncrono)Blob (asíncrono, basado en callback)
Uso de memoriaMayor (Base64 añade ~33% de sobrecarga)Menor (representación binaria)
Mejor paraImágenes pequeñas, incrustaciones rápidasCargas, imágenes grandes

toDataURL() es síncrono y conveniente, pero carga toda la cadena codificada en memoria de una vez. La codificación Base64 aumenta el tamaño de los datos aproximadamente un 33% en comparación con el binario original. Para imágenes grandes, esto importa.

toBlob() es asíncrono y produce un Blob binario directamente, lo cual es más eficiente para cargas:

canvas.toBlob((blob) => {
  const formData = new FormData();
  formData.append('image', blob, 'export.jpg');
  fetch('/upload', { method: 'POST', body: formData });
}, 'image/jpeg', 0.85);

Usa Base64 solo cuando específicamente necesites una cadena—para incrustar en JSON, almacenar en un campo de texto o generar un URI de datos para una etiqueta <img>. Para cargas de archivos, toBlob() es la mejor opción.

Soporte de Formatos y el Parámetro de Calidad

PNG es el único formato de salida garantizado en todos los navegadores. El soporte para JPEG y WebP depende del entorno:

canvas.toDataURL('image/jpeg', 0.85); // calidad: 0 a 1, solo JPEG/WebP
canvas.toDataURL('image/webp', 0.9);  // no soportado en todos los navegadores

El parámetro de calidad no tiene efecto en PNG, que siempre es sin pérdida. Para JPEG, 0.85 es un valor predeterminado razonable que equilibra el tamaño del archivo y la calidad visual. Si pasas un formato no soportado, el navegador vuelve silenciosamente a PNG.

El Problema del Canvas Contaminado

Si dibujas una imagen de origen cruzado en un canvas sin la configuración CORS adecuada, el canvas se “contamina”. Llamar a toDataURL() o toBlob() en un canvas contaminado lanza un SecurityError.

Para evitar esto, el servidor que aloja la imagen debe enviar los encabezados CORS apropiados (Access-Control-Allow-Origin), y debes establecer crossOrigin en el elemento de imagen antes de asignar su src:

const image = new Image();
image.crossOrigin = 'anonymous';
image.src = 'https://other-origin.com/image.png';

El orden aquí es crítico. Establecer crossOrigin después de src puede hacer que el navegador comience a obtener sin la bandera CORS, lo que aún contamina el canvas. Las imágenes cargadas desde el mismo origen nunca son un problema. Las imágenes de origen cruzado sin encabezados CORS no pueden exportarse—no hay solución del lado del cliente.

Cuándo Tiene Sentido la Codificación Basada en Canvas

Usa el enfoque de canvas cuando necesites:

  • Redimensionar o recortar una imagen del lado del cliente antes de codificar
  • Aplicar filtros o transformaciones antes de exportar
  • Incrustar imágenes procesadas pequeñas como URIs de datos

Si solo necesitas leer un archivo local como Base64 sin ninguna manipulación de canvas, FileReader.readAsDataURL() es más simple y omite el canvas por completo.

Conclusión

La API Canvas te da control preciso sobre la codificación de imágenes en el navegador. Usa toDataURL() para imágenes pequeñas y casos de uso basados en cadenas, prefiere toBlob() para cargas y trabajo sensible al rendimiento, y siempre ten en cuenta CORS al trabajar con imágenes externas. Cuando no se requiere manipulación de píxeles, omite el canvas por completo y usa FileReader en su lugar.

Preguntas Frecuentes

Sí, puedes dibujar un SVG en un canvas y llamar a toDataURL(). Sin embargo, el SVG debe cargarse primero como un elemento Image, y las restricciones de origen cruzado aún aplican. El resultado exportado es un mapa de bits rasterizado, no un vector escalable. Cualquier recurso externo referenciado por el SVG, como fuentes o imágenes vinculadas, puede no renderizarse correctamente en el canvas.

Esto generalmente sucede porque toDataURL() se llama antes de que la imagen termine de cargar. Asegúrate de llamarlo dentro del manejador onload de la imagen. Una imagen negra también puede resultar de no establecer el ancho y alto del canvas para que coincidan con las dimensiones de la imagen de origen antes de dibujar, lo que deja el canvas en su tamaño predeterminado de 300 por 150 píxeles.

No hay un límite formal en la especificación, pero los navegadores imponen restricciones prácticas. Los canvas muy grandes pueden producir cadenas Base64 que consumen memoria significativa o causan que la pestaña del navegador se ralentice o falle. Para imágenes grandes, toBlob() es la alternativa más segura y eficiente en memoria.

Los elementos canvas estándar no están disponibles en Web Workers porque dependen del DOM. Sin embargo, puedes usar OffscreenCanvas, que es soportado en navegadores modernos, para realizar dibujos y llamar a su método convertToBlob() dentro de un worker. Ten en cuenta que OffscreenCanvas no soporta toDataURL(), por lo que necesitarías convertir el Blob resultante a Base64 usando un FileReader si se requiere una cadena.

Complete picture for complete understanding

Capture every clue your frontend is leaving so you can instantly get to the root cause of any issue with OpenReplay — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data.

Check our GitHub repo and join the thousands of developers in our community.

OpenReplay