Cómo reparar el error getImageData () ¿El lienzo ha sido contaminado por datos de origen cruzado?

131

Mi código funciona muy bien en mi localhost pero no funciona en el sitio.

Recibí este error de la consola, para esta línea .getImageData(x,y,1,1).data:

Uncaught SecurityError: Failed to execute 'getImageData' on 'CanvasRenderingContext2D': The canvas has been tainted by cross-origin data. 

parte de mi código:

jQuery.Event.prototype.rgb=function(){
        var x =  this.offsetX || (this.pageX - $(this.target).offset().left),y =  this.offsetY || (this.pageY - $(this.target).offset().top);
        if (this.target.nodeName!=="CANVAS")return null;
        return this.target.getContext('2d').getImageData(x,y,1,1).data;
    }

Nota: mi url de imagen (src) es de una url de subdominio

Erfan Safarpoor
fuente
8
Recibo este error incluso cuando img.src es una url relativa local: "img / foo.png", entonces, ¿qué tiene de origen cruzado?
Sanford Staab
Consulte stackoverflow.com/a/16218015/5175433 : "Chrome no considera que los diferentes archivos locales provengan del mismo dominio".
A_P

Respuestas:

116

Como otros han dicho, está "contaminando" el lienzo al cargar desde un dominio de origen cruzado.

https://developer.mozilla.org/en-US/docs/HTML/CORS_Enabled_Image

Sin embargo, puede evitar esto simplemente configurando:

img.crossOrigin = "Anonymous";

Esto solo funciona si el servidor remoto establece el siguiente encabezado adecuadamente:

Access-Control-Allow-Origin "*"

El selector de archivos de Dropbox cuando se usa la opción "enlace directo" es un gran ejemplo de esto. Lo uso en oddprints.com para pasar imágenes de la url de la imagen remota de Dropbox a mi lienzo y luego enviar los datos de la imagen a mi servidor. Todo en javascript

quemaduras mate
fuente
1
Esto funcionó para corregir este error al cargar una imagen imgur en jsfiddle.net
Travis J
3
funcionó para mí si pongo crossorigin primero, luego establezco la fuente y luego accedo a la carga de datos
jones
2
¿Qué sucede si se trata de un archivo local y su aplicación no utiliza Internet en absoluto? Al igual que una aplicación webview de Android, la imagen está en la misma carpeta que el archivo html. Esto no lo resuelve.
Curtis
44

Descubrí que tenía que usar .setAttribute('crossOrigin', '')y tenía que agregar una marca de tiempo a la cadena de consulta de la URL para evitar que una respuesta 304 careciera del Access-Control-Allow-Originencabezado.

Esto me da

var url = 'http://lorempixel.com/g/400/200/';
var imgObj = new Image();
imgObj.src = url + '?' + new Date().getTime();
imgObj.setAttribute('crossOrigin', '');
Kirby
fuente
3
Intentar esto sin un servidor en un archivo local proporciona: El acceso a la imagen en 'archivo: /// C: /.../img/colorPaletteCtrl.png? 1507058959277' desde el origen 'nulo' ha sido bloqueado por la política CORS: Respuesta no válida . Por lo tanto, el origen 'nulo' no tiene acceso permitido.
Sanford Staab
1
Simplemente usando lo imgObj.setAttribute('crossOrigin', '')resolvió para mí, ¡gracias! img.crossOrigin = "Anonymous"funciona bien (como se menciona aquí ). Los navegadores @SanfordStaab tratan los archivos locales como si estuvieran en un dominio diferente; de ​​lo contrario, los propietarios de sitios web podrían leer sus archivos locales. Debe solicitar imágenes del mismo dominio, o los encabezados en la respuesta a su solicitud deben decirle al navegador que está bien que el código de otros dominios lea ese archivo.
19

No podrá dibujar imágenes directamente desde otro servidor en un lienzo y luego usarlas getImageData. Es un problema de seguridad y el lienzo se considerará "contaminado".

¿Funcionaría para usted guardar una copia de la imagen en su servidor usando PHP y luego simplemente cargar la nueva imagen? Por ejemplo, podría enviar la URL al script PHP y guardarla en su servidor, luego devolver el nuevo nombre de archivo a su javascript de esta manera:

<?php //The name of this file in this example is imgdata.php

  $url=$_GET['url'];

  // prevent hackers from uploading PHP scripts and pwning your system
  if(!@is_array(getimagesize($url))){
    echo "path/to/placeholderImage.png";
    exit("wrong file type.");
  }

  $img = file_get_contents($url);

  $fn = substr(strrchr($url, "/"), 1);
  file_put_contents($fn,$img);
  echo $fn;

?>

Usaría el script PHP con algunos javascript ajax como este:

xi=new XMLHttpRequest();
xi.open("GET","imgdata.php?url="+yourImageURL,true);
xi.send();

xi.onreadystatechange=function() {
  if(xi.readyState==4 && xi.status==200) {
    img=new Image;
    img.onload=function(){ 
      ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
    }
    img.src=xi.responseText;
  }
}

Si lo usa getImageDataen el lienzo después de eso, funcionará bien.

Alternativamente, si no desea guardar la imagen completa, puede pasar las coordenadas x & y a su script PHP y calcular el valor rgba del píxel en ese lado. Creo que hay buenas bibliotecas para hacer ese tipo de procesamiento de imágenes en PHP.

Si desea utilizar este enfoque, avíseme si necesita ayuda para implementarlo.

edit-1: peeps señaló que el script php está expuesto y permite que internet lo use potencialmente de manera maliciosa. hay un millón de formas de manejar esto, una de las más simples es algún tipo de ofuscación de URL ... creo que las prácticas seguras de php merecen un google por separado; P

edit-2: por demanda popular, he agregado una verificación para asegurarme de que sea una imagen y no un script php (de: PHP verifica si el archivo es una imagen ).

jaya
fuente
15
Además: cargar la imagen y el script desde el sistema de archivos local genera el mismo problema.
Guy Dafny
2
eso no es muy seguro, alguien podría inundar maliciosamente su servidor con archivos grandes usando el script php que sería malo
LAX1DUDE
1
La respuesta @ LAX1DUDE mejoró con un simple control de seguridad
jumpjack
1
Gracias a este método, también es posible pasar una imagen al motor de OCR tesseract.js sin usar node.js (solo agregue la llamada a Tesseract.recognize (img) en la función img.onload ().
jumpjack
1
NECESITA validar el nombre del archivo, el tipo de archivo / mime, la ubicación y el tamaño de las cargas proporcionadas por los usuarios. Este código es vulnerable a varios ataques. Lo dejaré como un ejercicio para el lector, pero creo que es posible cargar archivos .php a la raíz del servidor web con este código.
hiburn8
4

Estaba viendo este error Chromemientras estaba probando mi código localmente. Me cambié Firefoxy ya no veo el error. Tal vez cambiar a otro navegador es una solución rápida.

Si está utilizando la solución dada en la primera respuesta, asegúrese de agregarla img.crossOrigin = "Anonymous";justo después de declarar la imgvariable (por ejemplo var img = new Image();).

Safwan
fuente
2

Su problema es que carga una imagen externa, es decir, desde otro dominio. Esto provoca un error de seguridad cuando intenta acceder a los datos de su contexto de lienzo.

axelduch
fuente
sí, obtengo una imagen del subdominio de carga ... ¿Qué hago?
Erfan Safarpoor
1
Bueno, podría usar un proxy para recuperar su imagen, este proxy estaría en el mismo dominio en el que está ejecutando su lienzo.
axelduch
1
Estoy cargando una nueva imagen desde Mi PC e intento cargarla en el lienzo y recibo este error. No estoy usando ningún tipo de imagen del servidor.
Piyush Dholariya
su computadora es vista por el servidor donde se ubica el script (pero no se ejecuta) como un dominio externo, ya que la imagen copiada en el lienzo se crea en su computadora, donde el script se ejecuta realmente.
Jumpjack
2

Estás "contaminando" el lienzo al cargar desde un dominio de origen cruzado. Mira este artículo de MDN:

https://developer.mozilla.org/en-US/docs/HTML/CORS_Enabled_Image

srquinn
fuente
agregué <IfModule mod_setenvif.c> <IfModule mod_headers.c> <FilesMatch "\. (cur | gif | ico | jpe? g | png | svgz? | webp) $"> SetEnvIf Origin ":" IS_CORS Header set Access- Control-Allow-Origin "*" env = IS_CORS </FilesMatch> </IfModule> </IfModule> para acceder a todos los dominios pero no funciona.
Erfan Safarpoor
1
¿En qué navegador estás probando? Y asegúrese de reiniciar Apache.
srquinn
no puedo reiniciar apache ... tengo una cuenta compartida de cpanel.
Erfan Safarpoor
2
No puedo ayudarte a configurar tu configuración de Apache, lo siento. También está fuera del alcance de esta pregunta. Publique sus problemas relacionados con la configuración de Apache en una nueva pregunta y la comunidad estará encantada de ayudarlo.
srquinn
1

Cuando trabaje en local agregue un servidor

Tuve un problema similar cuando trabajaba en local. Su URL será la ruta al archivo local, por ejemplo, archivo: ///Users/PeterP/Desktop/folder/index.html.

Tenga en cuenta que estoy en un MAC.

Obtuve esto instalando un servidor http globalmente. https://www.npmjs.com/package/http-server

Pasos:

  1. Instalación global: npm install http-server -g
  2. Ejecutar servidor: http-server ~/Desktop/folder/

PD: Supongo que tienes un nodo instalado, de lo contrario no llegarás muy lejos ejecutando comandos npm.

Anas
fuente
0

Como dice Matt Burns en su respuesta , es posible que deba habilitar CORS en el servidor donde se alojan las imágenes problemáticas.

Si el servidor es Apache, esto se puede hacer agregando el siguiente fragmento (desde aquí ) a su configuración de VirtualHost o a un .htaccessarchivo:

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

... si lo agrega a un VirtualHost, probablemente también necesite volver a cargar la configuración de Apache (por ejemplo, sudo service apache2 reloadsi Apache se está ejecutando en un servidor Linux)

Nick F
fuente
0

Mi problema estaba tan desordenado que solo codifiqué la imagen en base64 para asegurarme de que no hubiera problemas CORS

bumsoverboard
fuente
-2

Me encuentro con el mismo problema hoy, y lo resuelvo por el siguiente código.

código HTML:

<div style='display: none'>
  <img id='img' src='img/iak.png' width='600' height='400' />
</div>
<canvas id='iak'>broswer don't support canvas</canvas>

código js:

var canvas = document.getElementById('iak')
var iakImg = document.getElementById('img')
var ctx = canvas.getContext('2d')
var image = new Image()
image.src=iakImg.src
image.onload = function () {
   ctx.drawImage(image,0,0)
   var data = ctx.getImageData(0,0,600,400)
}

código como el anterior, y no hay ningún problema de dominio cruzado.

Karry
fuente
1
El valor src sigue siendo una fuente de origen cruzado, entonces, ¿cómo evita esto el problema?
Loveen Dyall
Este enfoque funciona cuando se ejecuta un archivo html local (sin servidor) como file: /// X: /htmlfile.html. La mayoría de las personas terminan sus declaraciones con ";". ¿Cuál es el punto de la declaración "var data = .."? Me gustaría una EXPLICACIÓN DE POR QUÉ esto evita el error de "dominio cruzado". Pero lo hace, gracias.
dcromley
doble karry inteligente
Mari Selvan
1
@dcromley "La mayoría de la gente termina sus declaraciones con;" No se recomienda el uso de punto y coma según los estándares. Los puntos y comas simplemente agregan más trabajo, se ven feos y el código funciona perfectamente sin ellos. Visite github.com/standard/standard
madprops
1
@madprops developer.mozilla.org dice "Las aplicaciones de JavaScript consisten en declaraciones con una sintaxis apropiada. Una sola declaración puede abarcar varias líneas. Pueden ocurrir varias declaraciones en una sola línea si cada declaración está separada por un punto y coma. Esta no es una palabra clave , pero un grupo de palabras clave ". Así que estoy en la facción "usar siempre". Ni siquiera sabía que había una facción "nunca". Supongo que este último también cree que la tierra es plana. (Es broma, gracias)
dcromley
-3

Estaba teniendo el mismo problema, y ​​para mí funcionó simplemente concatenando https:${image.getAttribute('src')}

Livia Rett
fuente
explique lo que hizo
Yehuda Schwartz