Как исправить ошибку 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', '');

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

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


да, я получаю изображение с поддомена загрузки ... Что мне делать?
— Эрфан Сафарпур,

1
Что ж, вы можете использовать прокси-сервер для получения вашего изображения, этот прокси-сервер будет в том же домене, что и тот, на котором вы запускаете свой холст.
— axelduch

1
Я загружаю новое изображение со своего компьютера и пытаюсь загрузить его на холст, но получаю эту ошибку. Я не использую какой-либо образ сервера.
— Пиюш Дхолария

Ваш компьютер воспринимается сервером, на котором находится сценарий (но не выполняется), как внешний домен, поскольку изображение, скопированное на холст, создается на вашем компьютере, где сценарий фактически выполняется.
— jumpjack

2

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

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


я добавил <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


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

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


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


пожалуйста, объясните, что вы сделали
— Иегуда Шварц
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.