Electronでローカルファイルの画像を表示したい

いま、生成AIで生成した画像を管理するためのアプリを作成中です。その中でローカルファイルに保存したファイルを表示したいと考えました。

しかし、絶対パスを記述すると表示されるが相対パスを使用すると画像が取得できないという問題が発生した。

以下で紹介する方法以外にも様々な方法を試している方々がいた。そんな中で今回はセキュリティと動作環境の柔軟性を重要視して以下のような方法を使用したのでご紹介します。

勉強し始めたばかりの分野なのでセキュリティが甘かったり動作環境が限定的だったりするかもしれません。なにか問題があればコメントしていただければ嬉しく思います。

メインプロセスでエンコードして渡す

今回使用したのはメインプロセスでbase64にエンコードしたデーターを渡すという方法だ。

手順としては以下のようになる。

  1. 取得したいファイルの名前(相対パス)をレンダラーから送る
  2. メインプロセスがローカルに保存されたファイルを読み込む
  3. 読み込んだファイルをbase64にエンコードして返す
  4. base64で受け取ったデータを画像として表示する

メインプロセス

index.ts
import { app, ipcMain } from 'electron';
// 省略

app.on('ready', () => {
    // メインプロセスとレンダラーの双方向通信にreadImgという名前で登録
    ipcMain.handle('readImg', async (event, path: string) => {
    return await readImg(path);
  })
});

// 省略
ImageReader.ts
import fs from 'node:fs';
import Path from 'node:path';

export async function readImg(path: string): Promise<Uint8Array> {
    
    // このソースコードがあるディレクトリからの相対パスを絶対パスに変換
    let absPath = Path.join(__dirname, '../../library/img', path);
    
    // ファイルからバッファを読み込む
    const buffer = await fs.promises.readFile(absPath);

    // バッファから Uint8Array を生成
    return new Uint8Array(buffer);
}

プリロード

preload.ts
contextBridge.exposeInMainWorld('electron', {
    // index.tsで登録されたメソッドをreadImgという名前でレンダラーに公開
    readImg: async (path: string) => {return await ipcRenderer.invoke('readImg', path)}
})

レンダラー

MainWindow.tsx
import React, { useEffect, useState } from 'react';

export default function MainWindow() {
    const [img, setImg] = useState(null);

    const getImg = async () => { //非同期処理にする
        // 画像を読み込む(戻り値はUint8Array型)
        let data = await window.electron.readImg('test.png');
        // Uint8Array型をBlob型に変換
        // 文字列をバイナリに変換
        let bobData = new Blob([data], {type: 'image/png'});
        // ファイreaderを作成
        let reader = new FileReader();
        // 読み込み完了時の処理を設定
        reader.onload = () => {
            setImg(reader.result);
        }
        // データをデータURLに変換
        reader.readAsDataURL(bobData);
    };

    getImg();

    return (
        <div>
            {/* 画像を表示 */}
            {img !== null ? (
                <img src={img} />
            ) : (
                // 画像を読み込んでいない場合
                <div>Failed to load</div>
            )}
        </div>
    );
}

コメント

タイトルとURLをコピーしました