Comment réparer l'erreur getImageData () Le canevas a été contaminé par des données d'origine croisée?

131

Mon code fonctionne très bien sur mon hôte local mais il ne fonctionne pas sur le site.

J'ai eu cette erreur de la console, pour cette ligne .getImageData(x,y,1,1).data:

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

une partie de mon code:

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;
    }

Remarque: mon URL d'image (src) provient d'une URL de sous-domaine

Erfan Safarpoor
la source
8
J'obtiens cette erreur même lorsque img.src est une URL relative locale: "img / foo.png" - alors qu'est-ce que l'origine croisée à ce sujet?
Sanford Staab
Voir stackoverflow.com/a/16218015/5175433 : "Chrome ne considère pas que différents fichiers locaux proviennent du même domaine."
A_P le

Réponses:

116

Comme d'autres l'ont dit, vous «souillez» le canevas en le chargeant à partir d'un domaine d'origines croisées.

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

Cependant, vous pourrez peut-être éviter cela en définissant simplement:

img.crossOrigin = "Anonymous";

Cela ne fonctionne que si le serveur distant définit correctement l'en-tête suivant:

Access-Control-Allow-Origin "*"

Le sélecteur de fichiers Dropbox lors de l'utilisation de l'option "lien direct" en est un excellent exemple. Je l'utilise sur oddprints.com pour aspirer des images de l'URL de l'image de dropbox distante, dans ma toile, puis soumettre les données d'image à mon serveur. Tout en javascript

brûlures mates
la source
1
Cela a permis de corriger cette erreur lors du chargement d'une image imgur dans jsfiddle.net
Travis J
3
a fonctionné pour moi si je mets crossorigin en premier, puis définissez la source, puis accédez aux données en charge
jones
2
Que faire s'il s'agit d'un fichier local et que votre application n'utilise pas du tout Internet? Comme une application de visualisation Web Android et l'image se trouve juste dans le même dossier que le fichier html. Cela ne résout pas le problème.
Curtis
44

J'ai trouvé que je devais utiliser .setAttribute('crossOrigin', '')et ajouter un horodatage à la chaîne de requête de l'URL pour éviter une réponse 304 sans en- Access-Control-Allow-Origintête.

Cela me donne

var url = 'http://lorempixel.com/g/400/200/';
var imgObj = new Image();
imgObj.src = url + '?' + new Date().getTime();
imgObj.setAttribute('crossOrigin', '');
Kirby
la source
3
Essayer cela sans serveur sur un fichier local donne: L'accès à l'image à 'file: /// C: /.../img/colorPaletteCtrl.png? 1507058959277' à partir de l'origine 'null' a été bloqué par la stratégie CORS: réponse invalide . L'origine 'null' n'est donc pas autorisée à accéder.
Sanford Staab
1
Simplement utiliser l'a imgObj.setAttribute('crossOrigin', '')résolu pour moi - merci! img.crossOrigin = "Anonymous"fonctionne également (comme mentionné ici ). Les navigateurs @SanfordStaab traitent les fichiers locaux comme appartenant à un domaine différent, sinon les propriétaires de sites Web pourraient lire vos fichiers locaux. Vous devez demander des images du même domaine, ou les en-têtes de la réponse à votre demande doivent indiquer au navigateur que le code d'autres domaines est autorisé à lire ce fichier.
19

Vous ne pourrez pas dessiner des images directement à partir d'un autre serveur dans un canevas, puis les utiliser getImageData. C'est un problème de sécurité et la toile sera considérée comme "corrompue".

Cela fonctionnerait-il pour vous d'enregistrer une copie de l'image sur votre serveur en utilisant PHP, puis de charger simplement la nouvelle image? Par exemple, vous pouvez envoyer l'URL vers le script PHP et l'enregistrer sur votre serveur, puis renvoyer le nouveau nom de fichier à votre javascript comme ceci:

<?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;

?>

Vous utiliseriez le script PHP avec du javascript ajax comme celui-ci:

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 vous utilisez getImageDatasur la toile après cela, cela fonctionnera bien.

Alternativement, si vous ne voulez pas enregistrer l'image entière, vous pouvez passer les coordonnées x & y à votre script PHP et calculer la valeur rgba du pixel de ce côté. Je pense qu'il existe de bonnes bibliothèques pour faire ce genre de traitement d'image en PHP.

Si vous souhaitez utiliser cette approche, faites-moi savoir si vous avez besoin d'aide pour la mettre en œuvre.

edit-1: peeps a souligné que le script php est exposé et permet à Internet de l'utiliser potentiellement de manière malveillante. il existe un million de façons de gérer cela, l'une des plus simples étant une sorte d'obfuscation d'URL ... Je pense que les pratiques php sécurisées méritent un google séparé; P

edit-2: à la demande générale, j'ai ajouté une vérification pour m'assurer qu'il s'agit d'une image et non d'un script php (à partir de: PHP vérifier si le fichier est une image ).

jaya
la source
15
En plus - Le chargement de l'image et du script à partir du système de fichiers local pose le même problème.
Guy Dafny
2
ce n'est pas très sécurisé, quelqu'un pourrait inonder votre serveur de fichiers volumineux à l'aide du script php, ce qui serait mauvais
LAX1DUDE
1
Réponse @ LAX1DUDE améliorée avec un simple contrôle de sécurité
jumpjack
1
grâce à cette méthode, il est également possible de passer une image au moteur OCR tesseract.js sans utiliser node.js (il suffit d'ajouter l'appel à Tesseract.recognize (img) dans la fonction img.onload ().
jumpjack
1
Vous DEVEZ valider le nom du fichier, le type de fichier / mime, l'emplacement et la taille des téléchargements fournis par les utilisateurs. Ce code est vulnérable à plusieurs attaques. Je vais le laisser comme exercice au lecteur, mais je pense qu'il est possible de télécharger des fichiers .php à la racine du serveur Web avec ce code.
hiburn8
4

Je voyais cette erreur pendant Chromeque je testais mon code localement. Je suis passé à Firefoxet je ne vois plus l'erreur. Peut-être que passer à un autre navigateur est une solution rapide.

Si vous utilisez la solution donnée dans la première réponse, assurez-vous d'ajouter img.crossOrigin = "Anonymous";juste après avoir déclaré la imgvariable (par exemple var img = new Image();).

Safwan
la source
2

Votre problème est que vous chargez une image externe, c'est-à-dire depuis un autre domaine. Cela provoque une erreur de sécurité lorsque vous essayez d'accéder aux données de votre contexte de canevas.

axelduch
la source
oui, je reçois une image du sous-domaine de téléchargement ... Que dois-je faire?
Erfan Safarpoor
1
Eh bien, vous pouvez utiliser un proxy pour récupérer votre image, ce proxy serait sur le même domaine que celui sur lequel vous exécutez votre canevas.
axelduch
1
Je télécharge une nouvelle image depuis mon ordinateur et j'essaye de la charger dans le canevas et j'obtiens cette erreur. Je n'utilise aucun type d'image serveur.
Piyush Dholariya
votre ordinateur est vu par le serveur sur lequel se trouve le script (mais pas exécuté) comme un domaine externe, car l'image copiée dans le canevas est créée sur votre ordinateur, où le script est réellement exécuté.
jumpjack
2

Vous «souillez» le canevas en le chargeant à partir d'un domaine d'origines croisées. Consultez cet article MDN:

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

srquinn
la source
J'ai ajouté <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> pour accéder à tous les domaines mais ne fonctionne pas!
Erfan Safarpoor
1
Dans quel navigateur testez-vous? Et assurez-vous d'avoir redémarré Apache.
srquinn
je ne peux pas redémarrer apache ... j'ai un compte partagé cpanel.
Erfan Safarpoor
2
Je ne peux pas vous aider à configurer votre configuration Apache, désolé. Son également hors de la portée de cette question. Postez vos problèmes concernant la configuration d'Apache dans une nouvelle question et la communauté se fera un plaisir de vous y aider.
srquinn
1

Lorsque vous travaillez en local, ajoutez un serveur

J'ai eu un problème similaire en travaillant sur local. Votre URL va être le chemin vers le fichier local, par exemple file: ///Users/PeterP/Desktop/folder/index.html.

Veuillez noter que je suis sur un MAC.

J'ai contourné cela en installant le serveur http globalement. https://www.npmjs.com/package/http-server

Pas:

  1. Installation globale: npm install http-server -g
  2. Exécutez le serveur: http-server ~/Desktop/folder/

PS: Je suppose que vous avez installé un nœud, sinon vous n'obtiendrez pas très loin les commandes npm.

Anas
la source
0

Comme le dit Matt Burns dans sa réponse , vous devrez peut-être activer CORS sur le serveur où les images problématiques sont hébergées.

Si le serveur est Apache, cela peut être fait en ajoutant l'extrait de code suivant (à partir d' ici ) à votre configuration VirtualHost ou à un .htaccessfichier:

<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 vous l'ajoutez à un VirtualHost, vous devrez probablement également recharger la configuration d'Apache (par exemple, sudo service apache2 reloadsi Apache fonctionne sur un serveur Linux)

Nick F
la source
0

Mon problème était tellement compliqué que j'ai simplement encodé l'image en base64 pour m'assurer qu'il ne pouvait pas y avoir de problèmes CORS

bumsoverboard
la source
-2

Je rencontre le même problème aujourd'hui, et je le résolve par le code suivant.

Code 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>

code 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)
}

code comme ci-dessus, et il n'y a pas de problème interdomaine.

Karry
la source
1
La valeur src est toujours une source d'origine croisée, comment cela évite-t-il le problème?
Loveen Dyall
Cette approche fonctionne lors de l'exécution d'un fichier html local (sans serveur) comme file: /// X: /htmlfile.html. La plupart des gens terminent leurs déclarations par ";". Quel est l'intérêt de l'instruction "var data = .."? Je voudrais une EXPLICATION DE POURQUOI cela évite l'erreur "cross-domain". Mais c'est le cas, merci.
dcromley
double smart karry
Mari Selvan
1
@dcromley "La plupart des gens terminent leurs déclarations par;" L'utilisation de points-virgules n'est pas recommandée selon les normes. Les points-virgules ajoutent simplement plus de travail, ont l'air moche et le code fonctionne parfaitement sans eux. Vérifiez github.com/standard/standard
madprops
1
@madprops developer.mozilla.org dit: "Les applications JavaScript sont constituées d'instructions avec une syntaxe appropriée. Une seule instruction peut s'étendre sur plusieurs lignes. Plusieurs instructions peuvent apparaître sur une seule ligne si chaque instruction est séparée par un point-virgule. Ce n'est pas un mot-clé , mais un groupe de mots clés. " Donc, je suis dans la faction «utiliser toujours». Je ne savais même pas qu'il y avait une faction «jamais». Je suppose que ce dernier pense également que la terre est plate? (Je plaisante - merci)
dcromley
-3

J'avais le même problème, et pour moi, cela fonctionnait simplement en concaténant https:${image.getAttribute('src')}

Livia Rett
la source
Veuillez expliquer ce que vous avez fait
Yehuda Schwartz