1. ホーム
  2. Web制作
  3. html5

キャンバス画像getImageData,toDataURLのクロスドメイン問題の解決方法を説明する。

2022-02-01 16:50:05

まず、画像サーバーにAccess-Control-Allow-Originを設定します。

例えば、Tencentはgtimg.com、Baiduはbdimg.com、あるいはTencent CloudやAli Cloudのサービスを利用するチームも多いようです。

メインページは別ドメインにあることが多く、キャンバス画像に対して getImageData() や toDataURL() の操作が必要な場合、クロスドメインの問題が出てきてしまい、レイヤーが複数になってしまうのです。

まず、第一段階として、画像サーバーにAccess-Control-Allow-Originの情報を設定する必要があります、例えば、以下のようなものです。

PHP が応答ヘッダーメッセージを追加する場合、* ワイルドカード文字は、任意のドメイン名を許可することを示します。

header("Access-Control-Allow-Origin: *");

または、ドメイン名を指定する。

header("Access-Control-Allow-Origin: www.zhangxinxu.com");

この時点で、ChromeにはAccess-Control-Allow-Origin関連のエラーメッセージは表示されませんが、他のクロスドメインエラーメッセージは表示されます。

次に、キャンバス画像getImageDataのクロスオリジン・クロスドメイン問題

クロスドメイン画像の場合、Webページで正しく表示できる範囲であれば、canvasのdrawImage()APIで描画することが可能です。しかし、さらに進んで getImageData() メソッドで画像の全ピクセル情報を取得しようとすると、ほとんどの場合エラーが発生します。

例として、以下のコードを使って、github上の自分のアバター画像の情報を取得します。

var canvas = document.createElement('canvas');
var context = canvas.getContext('2d');

var img = new Image();
img.onload = function () {
    context.drawImage(this, 0, 0);
    context.getImageData(0, 0, this.width, this.height);
};
img.src = 'https://avatars3.githubusercontent.com/u/496048?s=120&v=4';';


その結果、Chromeで以下のようなエラーが表示されました。

Uncaught DOMException: CanvasRenderingContext2D'で'getImageData'の実行に失敗しました。キャンバスがクロスオリジンデータで汚染されています。

Firefoxブラウザのエラーは。

SecurityErrorです。この操作は安全ではありません。

canvas.toDataURL()メソッドが使用された場合、報告されます。

HTMLCanvasElement'で'toDataURL'の実行に失敗しました。汚染されたキャンバスはエクスポートされない可能性があります。

実は原因は同じで、クロスドメインです。

では、この問題を解決する方法はあるのでしょうか?

crossOriginプロパティを試してみてはいかがでしょうか。

III. HTML crossOrigin属性によるリソースクロスドメイン問題の解決

HTML5では、<img>, <video>, <script> など、CORS(クロスオリジンリソース共有)に対応した属性を提供する要素があり、その提供属性の名称が crossOrigin 属性となります。

したがって、上記のクロスドメイン問題は、次のように処理することができます。

var canvas = document.createElement('canvas');
var context = canvas.getContext('2d');

var img = new Image();
img.crossOrigin = '';
img.onload = function () {
    context.drawImage(this, 0, 0);
    context.getImageData(0, 0, this.width, this.height);
};
img.src = 'https://avatars3.githubusercontent.com/u/496048?s=120&v=4';';



img.crossOrigin = ''を追加するだけで、ここでのJSコードは空の文字列を設定しますが、実際に機能するプロパティ値は匿名です。

crossOriginには、以下の2つの値を指定できます。

<テーブル キーワード 定義 匿名 その要素に対するクロスドメインリソースリクエストは、クレデンシャルフラグの設定を必要としない。 使用証明書 要素のクロスドメインリソースリクエストでは、credentials フラグを設定する必要があります。

ここで、crossOriginの属性値がuse-credentialsでない限り、'abc'のような文字を含む空文字列も含めてすべて匿名に解決されます。

例えば

img.crossOrigin = 'abc';
console.log(img.crossOrigin); // the result is 'anonymous'


もうひとつ、crossOrigin属性を持たないことと、crossOrigin="use-credentials"を設定することはどちらもデフォルトでクロスドメインエラーを報告しますが、性質が異なり、両者の間には大きな違いがあることに留意してください。

CrossOriginの互換性

IE11+ (IE Edge)、Safari、Chrome、Firefoxのブラウザに対応しています。IE9とIE10は、以下のスクリーンショットに示すように、セキュリティエラーSecurityErrorが報告されます。

IV. なぜcrossOrigin属性は、リソースのクロスドメイン問題を解決するのか?

crossOrigin=anonymousは、相手のサーバーに「匿名でない情報を持ってくる必要はない」と伝えるための相対的なものです。例えばクッキーとか、だから今のブラウザは間違いなく安全です。

誰かの家にドレスを取りに行くとしたら、crossOrigin=anonymousと相手に「私はドレスが欲しいだけで、他には何もいらない」と伝えるのと同じです。そうしないと、相手がドレスにバグを入れたりするかもしれないので、安全とは言えず、ブラウザがブロックしてしまうからです。

V. IE10のブラウザがcrossOriginをサポートしていない場合はどうなりますか?

画像を要求するとき、new Image()を直接経由するのではなく、ajaxとURL.createObjectURL()メソッドでカーブさせています。

コードはこのようになります。

var xhr = new XMLHttpRequest();
xhr.onload = function () {
    var url = URL.createObjectURL(this.response);
    var img = new Image();
    img.onload = function () {
        // At this point you can use canvas to do whatever you want with the img
        // ... code ...
        // Remember to free up memory when you run out of images
        URL.revokeObjectURL(url);
    };
    img.src = url;
};
xhr.open('GET', url, true);
xhr.responseType = 'blob';
xhr.send();


この方法はIE10だけでなく、もともとcrossOriginに対応していたすべてのブラウザでOKです。

Ajaxリクエストが1つ増えるだけで、うまくいくのです!

IEでは、リクエストされた画像が数千ピクセルと大きすぎる場合、画像の読み込みに失敗することがわかりました、ブロブサイズ制限を超えているのでしょう。

VI. 結論

同じような問題に遭遇した友人の助けになれれば幸いです。また、Scripting Houseをもっとサポートしてほしいです。