ActiveStorage+Amazon S3で管理している画像をフロント側で扱うときのCORSまわりでハマったことめも

みんなでつくるダンジョンのテストサーバはHeroku+Amazon S3で構成しており、画像の管理まわりにはActiveStorage(Ruby on Rails)を使っていますが、テストサーバに上げても「うまくうごかない~」となっていたのでした。

S3上にある画像をImageオブジェクトで取得して、Canvasをつかって加工するというJavaScriptプログラムを組んでいました。

let url = "https://s3.example.com/hoge.png";
let image = new Image;
image.crossOrigin = "anonymous";  // オリジン間のリクエストだよーということを伝えるために必要
image.onload = () => {
  let canvas = document.createElement("canvas");
  やりたい処理
}
image.src = url;

これで画像が取得できてめでたし、と思っていたのですが、ブラウザ上にはこんなエラーメッセージが出てしまっていました(URLまわりは実際のものとは異なります)。

Access to Image at 'https://s3.example.com' from origin 'https://heroku.example.com' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'https://heroku.example.com' is therefore not allowed access.

Access-Control-Allow-Origin がうまく設定されていないとのこと。実際にブラウザのデベロッパーツールを確認してみても、確かに Access-Control-Allow-Origin ヘッダが画像のレスポンスにありません。 (Amazon S3とみんなでつくるダンジョンのテストサーバ(=heroku)は異なるオリジンとなるので、リソースを共有するときにはCORSの設定(Access-Control-Allow-Origin)が必要)

もちろんS3のCORS設定は実施済みです。

ActiveStorageで管理しているリソース(今回の場合は画像)を先ほどのJavaScriptプログラムで参照するために、 Railsサーバ側で url_for 関数を使ってリソースのURLをつくるのですが、ここでつくったURLはRailsサーバを向いています。このURLにアクセスするとリダイレクトがかかり、S3を参照するURLが返されます。

railsguides.jp

S3はリクエストにOriginヘッダがないと Access-Control-Allow-Origin ヘッダ情報を返してくれませんが、リダイレクトによってこの辺がうまくいっていないのでは?と思ったのでした。

docs.aws.amazon.com

そこで、先ほどのプログラムを以下のようにしてみます。Imageタグでリダイレクトを受けるのではなく、一度fetchをつかってリダイレクト先のURLを受け取り、そこからImageオブジェクトで画像を取得します。

let url = "https://s3.example.com/hoge.png";
fetch(url).then( response => {
  let image = new Image;
  image.crossOrigin = "anonymous";
  image.onload = () => {
    let canvas = document.createElement("canvas");
    やりたい処理
  }
  image.src = response.url;
});

これで無事にS3からくる画像レスポンスに Access-Control-Allow-Origin ヘッダが付与されてJavaScript上で画像を参照できるようになりました。めでたしめでたし。

ではでは~。