Tech Sketch Bucket of Technical Chips by TIS Inc.

S3上の画像をCORSを利用してCanvasで使う

Pocket

Amazon S3でCross-Origin Resource Sharing(CORS)というものが使えるようになっていたようなので、クロスドメインで取得した画像をCanvasで利用する目的で実際に使ってみました。


Cross Origin Resource Sharingってなんだろう

ブラウザではセキュリティのためにSame Origin Policyによって、他のオリジン(プロトコルとドメインとポート番号の組)へのデータ送受信を原則禁じています。
このセキュリティ上の制限を回避するのには一般的には以下のような方式があります。

これに加えてCORSという手法が提案されて標準化される方向に向かっており、記事を書いている時点では勧告候補のようです。
CORSによって、自分のWebアプリケーションで他のオリジンのデータを自分のオリジンのデータのように取り扱うことができるようになります。

詳しくは W3CのCORSに関する文書 を見ると良いと思います。

画像に対して適用するよ

img要素は元からクロスドメインでも読み込むことができます。じゃあクロスドメインの制限がないのだからCORSなんて関係ないんじゃないの?と考えてしまうかもしれません。
実際に、img要素を利用して表示するだけの用途であればあまり関係ないといってよいと思います。

しかしcanvas要素にクロスドメインの画像データを利用することを考えると話が異なってきます。
このようなクロスドメインの画像データをCanvasに読み込むとCanvasからの画像出力が制限されてしまいます。その辺りの解説が こちらのページ で解説されており、データが取り出せなくなることを汚染と呼ぶようです。

このためCanvasを利用するWebサービスを作る上では、クロスドメインの画像データがCanvasを汚染しないようにすることが重要なことです。そのためにCORSの利用が解のひとつになります。

じゃあどうやんの

HTML5規格に対応しているブラウザでは、 crossOrigin属性 が追加されています。
リンク先の内容によると、crossOrigin属性を設定することで画像をCORSに対応した方法で取得することができます。

画像を保持するS3はCORSに対応しているので特に何もしませんが、ApacheやNginxを使う場合はCORSに対応する設定をする必要があるものと思われます。

S3にCORS設定してみるよ

構成

全体的にこんな構成にします。

cors_sample.png

構成図

S3バケットを2箇所用意するのはバケットごとにCORSを設定可能なためです。
今回はCORS設定をしないtech-sketch-nocorsバケットと設定をするtech-sketch-corsバケットを用意しました。
また、今回は図中のInternetの部分にも別のS3バケット「seike」を使います。

S3のCORS設定

それぞれのバケットのProperties→Permissions→Edit CORS ConfigurationでCORSの設定をします。

tech-sketch-nocorsバケットはデフォルトのS3の設定を利用します。
S3のバケット tech-sketch-nocorsの設定

ドメインはどこでもいいことになってますが、CORSを利用するためにAuthorizationが必要です。
特にAuthorizationしないのでこのバケットはCORSをしない設定です。

以下、今回のCORSが有効であるか確認用のtech-sketch-corsバケットの設定です。
S3のバケット tech-sketch-corsの設定

オリジンが seike.s3-website-ap-northeast-1.amazonaws.com であればGETを許可するという設定になってます。

上記のように設定して、 両方のバケットにいくつかの画像を配置してクロスドメインで取得させます。

HTMLを配置

というわけで、S3にHTMLファイルを起動してサンプルコードを配置します。

5つのimg要素でそれぞれ以下のように画像を取得しました。

  1. crossOrigin属性ありでCORS対応バケットから取得
  2. crossOrigin属性ありでCORS未設定バケットから取得
  3. crossOrigin属性なしでCORS対応バケットから取得
  4. crossOrigin属性なしでCORS未設定バケットから取得
  5. sameOriginから取得

また、それぞれの画像読み込み完了時に以下を試みます

  • img要素での画像表示
  • Canvasへの画像を描画
  • Canvasの内容をtoDataURL()で文字列として出力できるか確認。

これにより、CORSによりCanvasに入出力が自由にできることorできないことを確認します。

ブラウザで開いてみる

上記のHTMLを開くと以下のようになりました。

cors_chrometest.png

cors_fftest.png

ChromeとFirefoxで表示や挙動がやや違うようです。
読み込んだ5つのimg要素について以下の3点を確認しています。

  • 赤枠で囲ったimg要素で表示の可否
  • 青枠で囲ったcanvas要素でCanvasへの描画可否
  • 緑枠で囲ったtextarea要素でCanvasからの出力可否

キャプチャの内容からわかることとして概ね以下のことが言えると考えられます。
数字はキャプチャ内のリスト番号との対応です。

(1)crossOrigin属性をつけてCORS対応バケットから取得

  • CanvasへのコピーもCanvasからの出力(青枠部分)も正しく動作。
  • sameOriginのデータと同様に取り扱うことができる。

(2)crossOrigin属性をつけてCORS未対応バケットから取得

  • Firefoxではimg要素で表示すらされない。(2.の赤枠部分)
  • Chromeではimg要素の表示はされているが、画面を開いた瞬間にコンソールにエラー出力が発生しており適切な状態ではない。(2.の赤枠部分)

(3)crossOrigin属性なしでCORS対応バケットから取得

  • Canvasへの画像描画は可能、Canvasから出力不能。⇒ Canvasが汚染されている。

(4)crossOrigin属性なしでCORS未設定バケットから取得

  • Canvasへの画像描画は可能、Canvasから出力不能。⇒ Canvasが汚染されている。

(5)sameOrigin

  • 同一オリジンなので特に制限なしで利用できている。

このサンプルによりCORS対応したサーバにcrossOrigin属性を設定して画像を取得すると、sameOriginから取得した画像と同様にCanvasで扱えることをChromeとFirefoxで確認できました。

また、S3側のCORS設定の有無にかかわらず、img要素にcrossOrigin属性を設定せずに取得した画像はCORSが有効になっていないため、Canvasに読み込むと汚染されCanvasから出力不能になることが分かりました。

まとめ

というわけで、S3のCORS対応を利用してクロスドメインの画像をCanvasで自由に利用することが出来ました。

今回はS3を利用したため容易に実施できましたが、ApacheやNginx上に配置されている既存のリソースはサーバにCORSの設定が必要なので、簡単にCORSができる!とはなりませんがRevereseProxyやJSONPなどと比較してどの手法が楽にCORS実現できるかなど考える材料になると思います。

参考

Cross-Origin Resource Sharing
Mozilla Developer Network|CORS Enabled Image

エンジニア採用中!私たちと一緒に働いてみませんか?