Как исправить ошибку getImageData () Холст был испорчен данными из разных источников?

131

Мой код очень хорошо работает на моем локальном хосте, но не работает на сайте.

Я получил эту ошибку из консоли для этой строки .getImageData(x,y,1,1).data:

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

часть моего кода:

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

Примечание: мой URL-адрес изображения (src) взят с URL-адреса поддомена

Эрфан Сафарпур
источник
8
Я получаю эту ошибку, даже если img.src является локальным относительным URL-адресом: "img / foo.png" - так что в этом перекрестного происхождения?
Sanford
См. Stackoverflow.com/a/16218015/5175433 : «Chrome не считает, что разные локальные файлы получены из одного домена».
A_P

Ответы:

116

Как уже говорили другие, вы «заражаете» холст загрузкой из домена перекрестного происхождения.

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

Однако вы можете предотвратить это, просто установив:

img.crossOrigin = "Anonymous";

Это работает, только если удаленный сервер правильно устанавливает следующий заголовок:

Access-Control-Allow-Origin "*"

Выбора файлов Dropbox при использовании опции «прямая ссылка» является прекрасным примером этого. Я использую его на oddprints.com для переноса изображений с URL удаленного изображения Dropbox на свой холст, а затем отправляю данные изображения обратно на мой сервер. Все в javascript

Мэтт Бернс
источник
1
Это помогло исправить эту ошибку при загрузке изображения imgur в jsfiddle.net
Трэвис Дж
3
у меня сработало, если я сначала поставил crossorigin, затем установил источник, а затем получил доступ к данным onload
Джонс
2
Что, если это локальный файл, а ваше приложение вообще не использует Интернет? Как и приложение для просмотра веб-страниц для Android, изображение находится в той же папке, что и файл html. Это не решает.
Curtis
44

Я обнаружил, что мне пришлось использовать .setAttribute('crossOrigin', '')и мне пришлось добавить метку времени к строке запроса URL-адреса, чтобы избежать ответа 304 без Access-Control-Allow-Originзаголовка.

Это дает мне

var url = 'http://lorempixel.com/g/400/200/';
var imgObj = new Image();
imgObj.src = url + '?' + new Date().getTime();
imgObj.setAttribute('crossOrigin', '');
Kirby
источник
3
Попытка этого без сервера в локальном файле дает: Доступ к изображению в 'file: /// C: /.../img/colorPaletteCtrl.png? 1507058959277' из источника 'null' заблокирован политикой CORS: недопустимый ответ , Следовательно, к источнику 'null' доступ не разрешен.
Sanford
1
Просто использование imgObj.setAttribute('crossOrigin', '')решило это для меня - спасибо! img.crossOrigin = "Anonymous"также работает (как упоминалось здесь ). @SanfordStaab Браузеры рассматривают локальные файлы как находящиеся в другом домене, в противном случае владельцы веб-сайтов могли бы читать ваши локальные файлы. Вам нужно запрашивать изображения из того же домена, или заголовки в ответе на ваш запрос должны сообщать браузеру, что код из других доменов может читать этот файл.
19

Вы не сможете рисовать изображения прямо с другого сервера на холсте, а затем использовать getImageData. Это проблема безопасности, и холст будет считаться "испорченным".

Сработает ли для вас сохранить копию изображения на сервере с помощью PHP, а затем просто загрузить новое изображение? Например, вы можете отправить URL-адрес сценария PHP и сохранить его на своем сервере, а затем вернуть новое имя файла в свой javascript следующим образом:

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

?>

Вы бы использовали сценарий PHP с некоторым javascript ajax следующим образом:

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

Если после этого использовать getImageDataна холсте, все будет нормально.

В качестве альтернативы, если вы не хотите сохранять все изображение, вы можете передать координаты x и y в свой PHP-скрипт и вычислить значение rgba пикселя на этой стороне. Я думаю, что есть хорошие библиотеки для такой обработки изображений в PHP.

Если вы хотите использовать этот подход, дайте мне знать, если вам понадобится помощь в его реализации.

edit-1: peeps указали, что скрипт php открыт и позволяет Интернету потенциально использовать его злонамеренно. есть миллион способов справиться с этим, один из самых простых - это своего рода обфускация URL ... я считаю, что безопасные методы php заслуживают отдельного Google; P

edit-2: по многочисленным просьбам я добавил проверку, чтобы убедиться, что это изображение, а не сценарий php (от: Проверка PHP, является ли файл изображением ).

джайа
источник
15
В качестве дополнения - загрузка изображения и скрипта из локальной файловой системы вызывает ту же проблему.
Guy Dafny
2
это не очень безопасно, кто-то может злонамеренно залить ваш сервер большими файлами, используя скрипт php, что было бы плохо
LAX1DUDE
1
@ LAX1DUDE ответ улучшен с помощью простой проверки безопасности
jumpjack
1
благодаря этому методу также можно передать изображение в механизм OCR tesseract.js без использования node.js (просто добавьте вызов Tesseract.recognize (img) в функцию img.onload ().
jumpjack
1
Вам НЕОБХОДИМО проверить имя файла, тип файла / mime, расположение и размер загрузок, предоставленных пользователями. Этот код уязвим для нескольких атак. Я оставлю это в качестве упражнения для читателя, но я считаю, что с помощью этого кода можно загружать файлы .php в корень веб-сервера.
hiburn8
4

Я видел эту ошибку, Chromeпока тестировал свой код локально. Я переключился на Firefoxи больше не вижу ошибки. Возможно, переход на другой браузер - быстрое решение.

Если вы используете решение, указанное в первом ответе, убедитесь, что вы добавили его img.crossOrigin = "Anonymous";сразу после объявления imgпеременной (например var img = new Image();).

Сафван
источник
2

Ваша проблема в том, что вы загружаете внешнее изображение, то есть из другого домена. Это вызывает ошибку безопасности при попытке доступа к каким-либо данным контекста холста.

axelduch
источник
да, я получаю изображение с поддомена загрузки ... Что мне делать?
Эрфан Сафарпур,
1
Что ж, вы можете использовать прокси-сервер для получения вашего изображения, этот прокси-сервер будет в том же домене, что и тот, на котором вы запускаете свой холст.
axelduch
1
Я загружаю новое изображение со своего компьютера и пытаюсь загрузить его на холст, но получаю эту ошибку. Я не использую какой-либо образ сервера.
Пиюш Дхолария
Ваш компьютер воспринимается сервером, на котором находится сценарий (но не выполняется), как внешний домен, поскольку изображение, скопированное на холст, создается на вашем компьютере, где сценарий фактически выполняется.
jumpjack
2

Вы «заражаете» холст загрузкой из домена перекрестного происхождения. Ознакомьтесь с этой статьей MDN:

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

srquinn
источник
я добавил <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> для htaccess всех доменов, но не работает!
Эрфан Сафарпур,
1
В каком браузере вы тестируете? И убедитесь, что вы перезапустили Apache.
srquinn
я не могу перезапустить apache ... у меня есть общая учетная запись cpanel.
Erfan Safarpoor
2
Не могу помочь вам настроить установку Apache, извините. Это тоже выходит за рамки этого вопроса. Опубликуйте свои проблемы, связанные с настройкой Apache, в новом вопросе, и сообщество будет рад помочь вам в этом.
srquinn
1

При работе на локальном добавить сервер

У меня была аналогичная проблема при работе с local. Ваш URL-адрес будет путем к локальному файлу, например file: ///Users/PeterP/Desktop/folder/index.html.

Обратите внимание, что я использую MAC.

Я обошел это, установив http-сервер глобально. https://www.npmjs.com/package/http-server

шаги:

  1. Глобальная установка: npm install http-server -g
  2. Запускаем сервер: http-server ~/Desktop/folder/

PS: Я предполагаю, что у вас установлен узел, иначе вы не получите очень далеко от команд npm.

Анас
источник
0

Как сказал Мэтт Бернс в своем ответе , вам может потребоваться включить CORS на сервере, на котором размещены проблемные изображения.

Если сервер Apache, это можно сделать, добавив следующий фрагмент ( отсюда ) либо в вашу конфигурацию VirtualHost, либо в .htaccessфайл:

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

... при добавлении его в VirtualHost вам, вероятно, потребуется также перезагрузить конфигурацию Apache (например, sudo service apache2 reloadесли Apache работает на сервере Linux)

Ник Ф
источник
0

Моя проблема была настолько запутанной, что я просто закодировал изображение base64, чтобы убедиться, что не может быть никаких проблем с CORS

bumsoverboard
источник
-2

Сегодня я сталкиваюсь с той же проблемой и решаю ее следующим кодом.

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>

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

код, подобный приведенному выше, и междоменной проблемы нет.

Karry
источник
1
Значение src по-прежнему является источником перекрестного происхождения, так как это избежать проблемы?
Loveen Dyall
Этот подход работает при запуске локального файла HTML (без сервера), например file: /// X: /htmlfile.html. Большинство людей заканчивают свои утверждения знаком «;». В чем смысл оператора "var data = .."? Я хотел бы ОБЪЯСНЕНИЯ, ПОЧЕМУ это позволяет избежать "междоменной" ошибки. Но это так, спасибо.
dcromley 05
double smart karry
Mari Selvan
1
@dcromley "Большинство людей заканчивают свои утверждения;" В соответствии со стандартами использование точки с запятой не рекомендуется. Точки с запятой просто добавляют больше работы, выглядят некрасиво, и код отлично работает без них. Проверьте github.com/standard/standard
madprops
1
@madprops developer.mozilla.org говорит: «Приложения JavaScript состоят из операторов с соответствующим синтаксисом. Один оператор может занимать несколько строк. Несколько операторов могут находиться в одной строке, если каждый оператор разделяется точкой с запятой. Это не ключевое слово. , но группа ключевых слов ". Так что я принадлежу к фракции "Всегда использовать". Я даже не знал, что существует фракция «никогда». Я полагаю, последние тоже считают, что Земля плоская? (
Шучу
-3

У меня была такая же проблема, и для меня это сработало, просто объединив https:${image.getAttribute('src')}

Ливия Ретт
источник
пожалуйста, объясните, что вы сделали
Иегуда Шварц