Esta página ha sido traducida del inglés por la comunidad. Aprende más y únete a la comunidad de MDN Web Docs.

View in English Always switch to English

Usar imágenes de origen cruzado en un canvas

HTML ofrece un atributo crossorigin para las imágenes que, combinado con una cabecera CORS adecuada, permite que las imágenes definidas con el elemento <img> y cargadas desde orígenes externos se usen en un <canvas> como si se hubieran cargado desde el origen actual.

Consulta Atributos de configuración CORS para más información sobre cómo se usa el atributo crossorigin.

Seguridad y canvas contaminados

Dado que los píxeles del mapa de bits de un canvas pueden provenir de fuentes muy diversas, incluidas imágenes o videos obtenidos de otros servidores, es inevitable que surjan problemas de seguridad.

En cuanto dibujes en un canvas cualquier dato cargado desde otro origen sin la aprobación de CORS, el canvas queda contaminado. Un canvas contaminado ya no se considera seguro, y cualquier intento de recuperar datos de imagen desde él generará una excepción.

Si el origen del contenido externo es un elemento HTML <img> o SVG <svg>, no se permite intentar recuperar el contenido del canvas.

Si el contenido externo proviene de una imagen obtenida mediante HTMLCanvasElement o ImageBitMap, y el origen de la imagen no cumple las reglas del mismo origen, los intentos de leer el contenido del canvas quedan bloqueados.

Llamar a cualquiera de los siguientes métodos en un canvas contaminado producirá un error:

Intentar cualquiera de estas acciones cuando el canvas está contaminado hará que se lance un SecurityError. Esto protege a los usuarios de la exposición de datos privados mediante el uso de imágenes para extraer información de sitios web remotos sin permiso.

Almacenar una imagen desde un origen externo

En este ejemplo, queremos permitir que las imágenes de un origen externo sean recuperadas y guardadas en el almacenamiento local. Implementar esto requiere configurar el servidor, así como escribir código para el propio sitio web.

Configuración del servidor web

Lo primero que necesitamos es un servidor configurado para alojar imágenes con la cabecera Access-Control-Allow-Origin configurada para permitir el acceso de origen cruzado a los archivos de imagen.

Supongamos que el sitio se sirve con Apache. Fíjate en el archivo de configuración del servidor Apache para imágenes CORS de HTML5 Boilerplate, mostrado a continuación:

apacheconf
<IfModule mod_setenvif.c>
  <IfModule mod_headers.c>
    <FilesMatch "\.(avifs?|bmp|cur|gif|ico|jpe?g|jxl|a?png|svgz?|webp)$">
      SetEnvIf Origin ":" IS_CORS
      Header set Access-Control-Allow-Origin "*" env=IS_CORS
    </FilesMatch>
  </IfModule>
</IfModule>

En resumen, esta configuración permite acceder a los archivos gráficos (los que tienen las extensiones ".bmp", ".cur", ".gif", ".ico", ".jpg", ".jpeg", ".png", ".svg", ".svgz" y ".webp") de origen cruzado desde cualquier parte de internet.

Implementar la función de guardado

Ahora que el servidor está configurado para permitir la recuperación de las imágenes de origen cruzado, podemos escribir el código que permite guardarlas en el almacenamiento local, como si fueran servidas desde el mismo dominio en el que se ejecuta el código.

La clave es usar el atributo crossorigin configurando crossOrigin en el HTMLImageElement en el que se cargará la imagen. Esto indica al navegador que solicite acceso de origen cruzado al descargar los datos de la imagen.

Iniciar la descarga

El código que inicia la descarga (por ejemplo, cuando se hace clic en un botón "Descargar") es el siguiente:

js
function startDownload() {
  let imageURL = "https://mdn.github.io/shared-assets/images/examples/mdn.svg";
  let imageDescription = "Logotipo de un dinosaurio frente a un mapa";

  downloadedImg = new Image();
  downloadedImg.crossOrigin = "anonymous";
  downloadedImg.addEventListener("load", imageReceived);
  downloadedImg.alt = imageDescription;
  downloadedImg.src = imageURL;
}

Aquí usamos una URL escrita directamente en el código (imageURL) y un texto descriptivo asociado (imageDescription), pero esto podría provenir fácilmente de cualquier lugar. Para iniciar la descarga de la imagen, creamos un nuevo objeto HTMLImageElement utilizando el constructor Image(). Luego, la imagen se configura para permitir la descarga de origen cruzado estableciendo su atributo crossOrigin en "anonymous" (es decir, permitir la descarga de la imagen de origen cruzado sin autenticación). Se añade un detector de eventos para el evento load que se dispara en el elemento de imagen, lo que indica que ya se recibieron los datos de la imagen. También se añade un texto alternativo a la imagen; aunque <canvas> no admite el atributo alt, ese valor puede usarse para definir un aria-label o el contenido interno del canvas.

Por último, el atributo src de la imagen se establece con la URL de la imagen que se va a descargar; esto hace que la descarga comience.

Recibir y guardar la imagen

El código que gestiona la imagen recién descargada se encuentra en el método imageReceived():

js
function imageReceived() {
  const canvas = document.createElement("canvas");
  const context = canvas.getContext("2d");

  canvas.width = downloadedImg.width;
  canvas.height = downloadedImg.height;
  canvas.innerText = downloadedImg.alt;

  context.drawImage(downloadedImg, 0, 0);
  imageBox.appendChild(canvas);

  try {
    localStorage.setItem("saved-image-example", canvas.toDataURL("image/png"));
  } catch (err) {
    console.error(`Error: ${err}`);
  }
}

imageReceived() se llama para manejar el evento "load" del HTMLImageElement que recibe la imagen descargada. Este evento se dispara una vez que todos los datos descargados están disponibles. Comienza creando un nuevo elemento <canvas> que usaremos para convertir la imagen en una URL de datos, y obteniendo acceso al contexto de dibujo 2D del canvas (CanvasRenderingContext2D) en la variable context.

El tamaño del canvas se ajusta para que coincida con la imagen recibida, el texto interno se establece con la descripción de la imagen, y luego la imagen se dibuja en el canvas usando drawImage(). Luego, el canvas se inserta en el documento para que la imagen quede visible.

Ahora es el momento de guardar la imagen localmente. Para eso, usamos el mecanismo de almacenamiento local de la Web Storage API, al que se accede mediante la propiedad global localStorage. El método toDataURL() del canvas se usa para convertir la imagen en una URL data:// que representa una imagen PNG, que luego se guarda en el almacenamiento local mediante setItem().

Véase también