2022-01-08

Electron で画像を表示するのに詰まった

結局 base64 にエンコードした値をレンダープロセスに渡すようにした。


Electron アプリの開発中に起きた出来事。

環境

Electron のバージョンは 16.0.5 である。 webPreferencespreload の設定だけしており、他の設定はデフォルト値である。

UI 部分は React を使っている。

最終的にやったこと

画像を base64 でエンコードする。

const image = readFileSync("/path/to/image.jpg");
const encodedImage = image.toString("base64");

img タグを以下のように書く。
<img src={`data:image/jpg;base64,${encodedImage}`} />

これで画像が表示された。

実際はレンダラープロセスから Node.js の関数を呼ぶことはできない(nodeIntegration: false の場合)ので、エンコードされた画像を取得するための API を公開する必要がある。

実際の流れ

404 が返る

ローカルにある画像ファイルを表示するため、<src img={'/path/to/image.jpg'} /> のようなコンポーネントを書いた。 アプリを実行してみると、http://localhost:3000/path/to/image.jpg にリクエストを投げていて 404 エラーが返っていた。

スキーマを file にする

<img src="file:///path/to/image.jpg" /> に変えてみると、404 ではなく Not allowed to load local resource というエラーに変わった。 webPreferencesWebSecurity プロパティを false にすると良いらしいという情報を得たが false にするのは非推奨なので別の方法を探すことにした。

Security | Electron

protocol.interceptFileProtocol を使う

Electron の issue に今回の件そのままのものがあった。
9.0.0 does not display local images · Issue #23757 · electron/electron · GitHub

解決方法として挙げられていたのが上記の webSecurityfalse にする方法と、registerFileProtocol を使って何かする方法である。 後者の方法でなぜうまくいくかはわからないが、とりあえず試してみた。

protocol | Electron

app.whenReady().then(() => {
  protocol.registerFileProtocol('file', (request, callback) => {
    const pathname = request.url.replace('file:///', '');
    callback(pathname);
  });
});

上記のコードを書いて実行してみるが、結果は変わらずエラーも同じ。 console.log で登録した関数が実行されているか確かめたが、全く実行されなかった。

スキーマを file から http に変えてみるとメインプロセスのエントリポイントである index.js のリクエスト時には動くことが確認できた。 registerHttpProtocol などで実行してみるも、変わらず。

諦めた

エンコードした画像データを API で取得できるようにした。 もともとレンダラープロセスから API を使って画像のパスを取得していたので、そのような変更をするのには時間がかからなかった。

学び