Rust programming language

広告 Javascript/Typescript Rust Tech

【Rust+Tauri】フロントエンドへの画像送信高速化

この記事でわかること

Tauriでバックエンドからフロントエンドに高速で画像を送信する方法がわかります。

通常、Tauriでフロントエンドとバックエンドがデータのやり取りをする場合、CommandかEventが利用されます。

これらの方法でデータをフロントエンドに渡すと、一度JSONに変換してから受け渡されるので、送信が遅くなります。

特に画像データはデータ量が大きいので、変換に大きく時間が取られます。

この辺の話は、下記のURLページに記載があります。

https://tauri.app/v1/references/architecture/inter-process-communication

今回は、Custom Protocolを利用して、画像データを送ります。

Custom Protocolとは?

よく利用するURLは次のような形をしていると思います。

https://www.google.co.jp

こちらは、Googleの検索ページのURLになります。

分解すると、「https」「//www.google.co.jp」になります。

「https」は、スキームといって通信プロトコルを表しています。

「//www.google.co.jp」は、場所だったりアクションだったりします(このケースは場所ですね)。

つまり、このケースではHTTPSのプロトコルを使って、//www.google.co.jpに紐づいた処理をするという意味合いになります。

実際に、これをブラウザーに入力すると、Googleのサイトが表示されますね。

他にも、この仕組みでアプリを起動させたりもできます。

Custom Protocolは、カスタムなので、スキームの部分(上で言うとhttpsのところですね)が、自分で作ったものに変更できます。

なので、Custom Protocolを定義して、それを使って画像を送信します

どうして、そんな面倒臭いことをやらないといけないのかは、おそらくブラウザの仕組み上そうするしかないのかと思います。

準備

tauriを動かせる前提で話をします。

Tauriの準備

実装していきましょう

1. プロジェクトの作成

本プロジェクトは、

  • パッケージ管理:npm
  • UIフレームワーク:React(Typescript)

を使用します。

また、プロジェクト名は「tauri-custom-protocol」としています。


$ npm create tauri-app@latest tauri-custom-protocol
Need to install the following packages:
  create-tauri-app@3.13.17
Ok to proceed? (y) y
✔ Choose which language to use for your frontend · TypeScript / JavaScript - (pnpm, yarn, npm, bun)
✔ Choose your package manager · npm
✔ Choose your UI template · React - (https://react.dev/)
✔ Choose your UI flavor · TypeScript

Template created! To get started run:
  cd tauri-custom-protocol
  npm install
  npm run tauri dev

2. Rustのimageライブラリの追加

Rustで画像を読み込むのに、今回は「image」ライブラリを利用します。

なので、 Cargo.tomlに下記を追記します。


...
[dependencies]
tauri = { version = "1", features = [ "fs-all"] }
serde = { version = "1", features = ["derive"] }
serde_json = "1"
image = "0.23.14"  <-- 追加
...

3. tauriのCustomProtocolの実装


// Prevents additional console window on Windows in release, DO NOT REMOVE!!
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]

use image::io::Reader as ImageReader;
use tauri::http::ResponseBuilder;

fn main() {
    tauri::Builder::default()
        // custom protocol
        .register_uri_scheme_protocol("reqimg", move |_app, _request| {
            // read the png image file
            let image = ImageReader::open("icons/Square310x310Logo.png")
                .unwrap()
                .decode()
                .unwrap()
                .into_bytes();

            ResponseBuilder::new()
                .mimetype("arrayBuffer")
                .status(200)
                .header("Access-Control-Allow-Origin", "*")
                .body(image)
        })
        .run(tauri::generate_context!())
        .expect("error while running tauri application");
}

register_uri_scheme_protocolでCustomProtocoの登録をしています。

今回は、デフォルトで生成される icons以下の画像ファイルを配信しようと思います。

4. react(フロントエンド)での画像取得

今回のGUIにはボタンを用意して、押下したら画像が表示されるようにします。

App.tsxを下記のように書き換えてください。


import { useRef } from "react";
import "./App.css";

const App = () => {
  const canvasRef = useRef(null);

  const handleClick = async () => {
    const request = new Request("reqimg://test", {
      method: "GET",
      headers: {},
    });
    const response = await fetch(request).catch((reason) => {
      throw reason;
    });
    if (!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}`);
    }
    if (response.body === null) {
      throw new Error("Response body is null");
    }

    // Read the response as arrayBuffer
    const arrayBuffer = await response.arrayBuffer();
    const buffer = new Uint8ClampedArray(arrayBuffer);

    if (!canvasRef.current) {
      throw new Error("Canvas element not found");
    }

    if (buffer.length !== 310 * 310 * 4) {
      throw new Error(`Invalid buffer length: ${buffer.length}`);
    }

    const ctx = canvasRef.current?.getContext("2d");
    if (ctx) {
      console.log("Drawing image");
      const img = new ImageData(buffer, 310, 310);
      ctx.putImageData(img, 0, 0);
    }
  };

  return (
    
); }; export default App;

これで画像を取得できるようになりました。

下記の箇所で、CustomProtocolで画像をRequestしています。


const request = new Request("reqimg://test", {
  method: "GET",
  headers: {},
});

注意事項としては、WindowsだとCustomProtocolのURL仕様が違う?のか、reqimg://testではなくて、 https;//reqimg.test とする必要がありました。

実際の挙動

では、動かしてみましょう。


$ nom run tauri dev

成功すると、次の画面が表示されます。

「Open URL」をクリックすると、次のように画像が表示されます。

終わりに

今回は、tauriでバックエンドからフロントエンドに高速に画像を送信する方法を紹介しました。

CommandやEventよりも著しく速度が速くなります。今回はその比較はしませんでしたが、それぞれ実装してみると、体感できると思いますので、ぜひ試してください。

-Javascript/Typescript, Rust, Tech
-, , , , ,