Tauri はフロントエンドを brotli で圧縮して実行ファイルに埋め込みますが、JPEG・PNG・WebP はもともと圧縮済みなので、この段階ではほとんど縮みません。手元では 1.5MB の JPEG が 1.5MB のまま入りました。画像は自分で小さくしてから入れる必要があります。ここでは変換と縮小の手順、Tauri で画像を同梱する 3 つの置き場所と読み込み方、アイコンやトレイ画像で実行ファイルが膨らむ落とし穴を扱います。どの画像が大きいかの調べ方は アプリのバンドルサイズを分析する、画像以外の削減は アプリを軽量化する を参照してください。
前提条件
変換には sharp-cli(画像処理ライブラリ sharp のコマンド版)を開発用に入れます。配布物には入りません。
npm install -D sharp-cli
bundle.resources に置いた画像を表示する場合だけ、asset プロトコルを有効にします(2 章)。プラグインは不要で、パスの解決に使う resolveResource() は core:default の範囲で呼べます。3 章の Rust の例は tauri の機能を 2 つ使います。
[dependencies]
tauri = { version = "2", features = ["tray-icon", "image-png"] }
1. 画像そのものを小さくする
効くのは寸法と形式の 2 つです。アプリのウィンドウは広くても 1920px 程度なので、4K の写真はそのままでは無駄が大きく、拡大率 200% の画面を考えても「表示する大きさの 2 倍」で足ります。形式は写真なら WebP の非可逆、透過のあるロゴやイラストなら WebP の可逆が扱いやすく、線画のアイコンは SVG にすると埋め込み時の圧縮も効きます。
# 元画像は images/ に置き、幅 1920 を超えるものだけ縮めて WebP(品質 80)で書き出す
npx sharp -i "images/*.jpg" -o "src/assets/{name}.webp" -q 80 resize 1920 --withoutEnlargement
# 透過のあるロゴは可逆の WebP にする
npx sharp -i images/logo.png -o src/assets/ -f webp --lossless
3840×2400 の写真(高画質の JPEG)で試した結果です。元の JPEG の画質が高いほど差が大きく出ます。
| 変換 | 大きさ |
|---|---|
| 元の JPEG(3840×2400) | 1,565 KB |
| WebP 品質 80 | 177 KB |
| AVIF 品質 50 | 89 KB |
| 幅 1920 に縮小 + JPEG 品質 80 | 116 KB |
| 幅 1920 に縮小 + WebP 品質 80 | 75 KB |
変換はビルドのたびに行わず、変換した結果をリポジトリに入れます。元画像を public/ に残すと、使っていなくても実行ファイルに埋め込まれるので、images/ のような dist/ に出ない場所に置きます。
2. 置き場所と読み込み方を選ぶ (TypeScript)
| 置き場所 | 配布物での扱い | 読み込み方 |
|---|---|---|
src/assets/ から import | ハッシュ付きの名前で dist/ に出力され、実行ファイルに埋め込まれる。4KB 未満は JS の中に base64 で入る | import した URL |
public/ | そのまま dist/ にコピーされ、埋め込まれる。Vite の出力表に出ない | /photo.webp のような絶対パス |
bundle.resources | 実行ファイルの外に別ファイルとして同梱される | convertFileSrc(await resolveResource(...)) |
画面の部品として使う少数の画像は import、ギャラリーの写真や動画のように数が多い・大きいものは bundle.resources に出すのが目安です。インストーラーの大きさはどちらでもほぼ同じですが、埋め込んだファイルは読み込むたびに全体を展開して渡されるのに対し、asset プロトコルはファイルを直接読み、部分的な読み込みにも応じます。
import { convertFileSrc } from '@tauri-apps/api/core';
import { resolveResource } from '@tauri-apps/api/path';
import hero from './assets/hero.webp'; // ビルド時に dist/ へ出力される URL
document.querySelector<HTMLImageElement>('#hero')?.setAttribute('src', hero);
// bundle.resources の photos/ に同梱した画像を表示する
export async function showPhoto(img: HTMLImageElement, name: string) {
const path = await resolveResource(`photos/${name}`); // インストール先の絶対パス
img.src = convertFileSrc(path); // WebView から読める asset プロトコルの URL にする
}
tauri.conf.json では同梱と asset プロトコルを設定します。scope は同梱したフォルダーだけに絞ります。CSP を設定しているなら img-src に asset: と http://asset.localhost を足します。
{
"app": {
"security": {
"csp": "default-src 'self' ipc: http://ipc.localhost; img-src 'self' asset: http://asset.localhost",
"assetProtocol": { "enable": true, "scope": ["$RESOURCE/photos/**"] }
}
},
"bundle": {
"resources": { "resources/photos/": "photos/" }
}
}
assetProtocol.enable を true にすると tauri クレートの protocol-asset 機能が必要になり、tauri dev / tauri build が Cargo.toml に自動で足します。
3. アイコンとトレイ画像で膨らませない (Rust)
Rust 側の tauri::include_image! は、PNG を展開した画素のまま(1 ピクセル 4 バイト)実行ファイルに入れます。32×32 なら 4KB ですが、512×512 だと約 1MB です。トレイ用には小さい PNG を使い、大きな画像が要るときは PNG のまま埋め込んで実行時に展開する Image::from_bytes() にします(image-png 機能のぶんコードは増えます)。
use tauri::image::Image;
use tauri::tray::TrayIconBuilder;
// 展開後 32 x 32 x 4 = 4KB。パスは src-tauri からの相対
const TRAY_ICON: Image<'static> = tauri::include_image!("icons/32x32.png");
// PNG のまま埋め込み、使うときに展開する(features = ["image-png"])
fn about_logo() -> tauri::Result<Image<'static>> {
Image::from_bytes(include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), "/icons/128x128@2x.png")))
}
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
tauri::Builder::default()
.setup(|app| {
TrayIconBuilder::new().icon(TRAY_ICON).build(app)?;
let _logo = about_logo()?; // 例: 「このアプリについて」の画面で使う
Ok(())
})
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
ウィンドウの既定のアイコンも同じ仕組みで埋め込まれます。Windows では bundle.icon の最初の .ico の先頭の画像、それ以外の OS では最初の .png が使われるので、配列の先頭は icons/32x32.png のような小さいものにしておきます。アイコン一式の作り方は アプリアイコンを画像から生成する を参照してください。
動作確認
npm run tauri build の前後で実行ファイルの大きさを比べます(測り方は perf-001)。手元の例では、3840×2400 の JPEG(1.5MB)を dist/ に入れると実行ファイルが約 1.5MB 増え、幅 1920 の WebP(75KB)に替えると増加分も約 75KB になりました。include_image! を 32px から 512px の画像に替えると、約 1MB 増えます。
bundle.resources の画像は、npm run tauri dev で開発者ツールの Network タブを開くと、Windows では次のような URL で読み込まれているのが見えます。
http://asset.localhost/C%3A%5Cwork%5Cmy-app%5Csrc-tauri%5Ctarget%5Cdebug%5Cphotos%5Cbeach.webp
よくあるエラーと対処法
resourcesの画像が表示されない(Network タブで 403):assetProtocol.enableがfalseか、scopeが同梱先を含んでいません。$RESOURCE/photos/**のように同梱先に合わせます。CSP を設定しているならimg-srcも確かめます。- 「The
tauridependency features on theCargo.tomlfile does not match the allowlist defined undertauri.conf.json.」: asset プロトコルを有効にしたのにcargo buildを直接実行しています。tauri buildを使うか、Cargo.tomlの tauri にprotocol-assetを足します。 - 「icon …/icon.png is not RGBA」:
include_image!にパレット形式(減色した)PNG を渡しています。圧縮ツールで減色した PNG はここでは使えないので、RGBA で書き出したものを使います。 - 「Provided Image path "…" doesn't exists」:
include_image!のパスはsrc-tauriを基準に書きます。
OS ごとの違いと注意点
- Windows: WebView2 は WebP・AVIF を表示できます。asset プロトコルの URL は
http://asset.localhost/で始まります。 - macOS: WebView の対応は OS の版に従い、WebP は macOS 11 以降、AVIF は macOS 13 以降が目安です。
bundle.macOS.minimumSystemVersionの既定は10.13なので、古い macOS も対象にするなら<picture>で PNG や JPEG を控えに置くか、最低バージョンを上げます。URL はasset://localhost/です。 - Linux: 対応形式はディストリビューションが提供する WebView の版で変わります。配布先と同じ環境で表示を確かめます。
.icoに入るのは 256px までで、.icnsは大きめになりがちです。アイコンは 1 回生成すれば済むので、元画像の画質は落とさずに作ります。
