FetchとFileAPI@chrome拡張開発

最近、いろいろとchrome拡張を作っているわけですが、その技術メモになります。実装方法について他にもあるかと思いますが、まずは備忘録として。

今回の課題

  • 画像を外部サーバーからとってきて登録したいが、クロスドメインのエラーではじかれて取得できない
  • 画像データをBlobもしくはArrayBufferで取得後、メッセージングできない
  • FileAPI使うためのダイアログにFileオブジェクトを登録できない

同じような悩みをお持ちの方がいれば参考になるかと思います。

クロスドメイン(CROSS ORIGIN)制約

javascriptから他ドメインのデータを動的に読み込んだりすることを禁止する制約です。fetchでデータを取得しようと思っても他ドメインだとエラーが出て成功しません。ただし、php等はクロスドメイン制約はないので、phpでラッパー関数を書いて同じドメイン内に置くことでjavascirptからクロスドメインのデータを取得することができます。

でもこれをやるとサーバーに置いたラッパー関数の処理負荷が急上昇しそうです。あと踏み台にされそう。加えて大量に同一ドメインからのアクセスがばらまかれるのでよろしくなさそうです。

なので今回はchrome拡張という特性を生かしてjavascriptで外部サーバーのデータを読みだそうと思います。

対応方法

構成としては、content scriptはクロスドメイン制約を受けるため、background scriptにfetchを持たせて、content script-background script間はメッセージでやり取りさせます。

ちょっと応用系の書き方されていますが↓が参考になります。

http://var.blog.jp/archives/69337442.html

ちなみに上記リンクの場合ですとArrayBufferをcontent scriptに応答する際に、通常の配列形式に変換してやり取りしています。

content scriptでは再度Array Bufferに直すようなことでバイナリ形式のfetchを実現しています。

ただ、テキスト形式のデータ読み込みであればこんな変換は必要なく、fetch取得データを単純に応答してあげればOKです。スクレイピングであれば受け取ったものを正規表現で抜いたり、jQueryに読ませてアクセスする等が考えられます。

画像データをArray Bufferで取得できない

上記の方法でクロスドメイン下でもfetchすることはできるようになりましたが、画像を取り込もうと思って苦戦しました。

http://www.366service.com/jp/qa/7b62eaf39313a39156c2a5524d7a4178

↑によると通常のバイナリ形式であればArrayBufferで行けるが、画像データになると少し手間を加えないといけないとのこと…。ちょっとやってみましたが、理解が追い付かず断念。

対応方法

ということでbase64urlエンコードでメッセージングすることにしました。

画像をbase64形式にする方法

background scriptでfetchした画像データをbase64urlエンコードしてメッセージ応答として返す。content scriptで受け取ったbase64urlをデコードしてFileオブジェクト化します。

https://www.it-swarm-ja.tech/ja/javascript/base64-string%e3%82%92%e3%83%95%e3%82%a1%e3%82%a4%e3%83%ab%e5%85%a5%e5%8a%9b%e3%83%95%e3%82%a9%e3%83%bc%e3%83%a0%e3%81%ae%e3%82%88%e3%81%86%e3%81%abjavascript%e3%83%95%e3%82%a1%e3%82%a4%e3%83%ab%e3%82%aa%e3%83%96%e3%82%b8%e3%82%a7%e3%82%af%e3%83%88%e3%81%ab%e5%a4%89%e6%8f%9b%e3%81%99%e3%82%8b%e6%96%b9%e6%b3%95%e3%81%af%ef%bc%9f/823728874/

base64url→Fileオブジェクト化は↑の方法1を参考にしました。

FileAPIにFileオブジェクトを登録できない

ローカルファイル等をアップロードする仕組みとしてHTML5で搭載されたFileAPIですがセキュリティの観点からユーザー操作以外でローカルデータを取得できないしようとなっております。当然といえば当然の処置ですね。

ただ、ローカルファイルでなければ何とかならなかといろいろ探したところFileListならば登録できることが判明しました!

ちなみに、先ほどbase64から作成したのはFileオブジェクト!これはそのまま登録できませんでした…

対応方法

FileAPIを使用するためにはFileListを作る必要があります。ただしFileListをnewすることはできません。

しかし、抜け道が!

現状のchromeであればDataTransferオブジェクトから作ることができます(IEは×)。

https://qiita.com/jkr_2255/items/1c30f7afefe6959506d2

おわりに

詳しい実装方法についてはリンク先をご覧いただければと思います。ただ、こういったfetchとFileAPIを自由に使えるようになるとちょっとした作業ってかなり効率化できる気がします。アイデア次第で時短も夢ではないので興味があればぜひ!