画像やDBファイルを配布物に同梱する

bundle.resources で画像や初期 DB を配布物に同梱し、resource_dir() と resolveResource() で読む。必要な権限、glob の書き方、書き込めない点の扱いも示す。

ビルド・配布 対象: Tauri 2.x 更新日: 読了目安: 約6分 build-009
目次
  1. 前提条件
  2. 1. tauri.conf.json でリソースを指定する
  3. 2. フロントエンドから読む (TypeScript)
  4. 3. バックエンドから読む (Rust)
  5. 動作確認
  6. よくあるエラーと対処法
  7. フロントから読むと not allowed になる
  8. ビルド時に glob に一致するファイルがない
  9. インストール後に DB へ書き込めない
  10. OS ごとの違いと注意点
  11. 関連レシピ

フロントエンドの dist/ に入れたファイルは WebView からしか見えません。Rust 側で開きたい初期データベースや辞書ファイルなどは、bundle.resources で「リソース」として同梱します。同梱したファイルは OS ごとに決まった場所に展開され、Rust からは app.path().resource_dir()、フロントからは resolveResource() で実パスを解決できます。

前提条件

Rust から読むだけならプラグインは不要です。フロントエンドから直接読む場合は fs プラグインと権限が必要です。

npm run tauri add fs

src-tauri/capabilities/default.json にリソース配下の読み取りを許可します。

{
  "permissions": [
    "core:default",
    "fs:allow-resource-read-recursive"
  ]
}

範囲を絞るなら fs:allow-read-text-file にスコープ { "path": "$RESOURCE/lang/*" } を付ける形でも構いません($RESOURCE がリソースディレクトリを指すパス変数です)。

1. tauri.conf.json でリソースを指定する

配列形式と、配置先を指定するオブジェクト形式があります。パスは src-tauri/ からの相対パスです。

{
  "bundle": {
    "resources": {
      "resources/lang/*.json": "lang/",
      "resources/seed.db": "db/seed.db",
      "../shared/images/": "images/"
    }
  }
}

配列形式 ["resources/**/*"] にすると、src-tauri/ からの相対パス構造がそのまま保たれます。glob は dir/(再帰)、dir/*(直下のファイルのみ)、dir/**/*(サブディレクトリ内もすべて)の 3 種を使い分けます。配列形式で ../ を使うと配置先が _up_ というディレクトリ名に置き換わるので、src-tauri/ の外を参照するときはオブジェクト形式で配置先を明示するのが無難です。

2. フロントエンドから読む (TypeScript)

import { resolveResource } from '@tauri-apps/api/path';
import { readTextFile, readFile } from '@tauri-apps/plugin-fs';

// 同梱した JSON を読む
async function loadLang(code: string) {
  const path = await resolveResource(`lang/${code}.json`);
  return JSON.parse(await readTextFile(path));
}

// 同梱した画像を <img> に表示する
async function loadImage(name: string): Promise<string> {
  const bytes = await readFile(await resolveResource(`images/${name}`));
  return URL.createObjectURL(new Blob([bytes]));
}

resolveResource は core:default に含まれる path 権限で使え、絶対パスを返します。画像が多い場合は convertFileSrc で asset プロトコル経由にする方が軽量ですが、app.security.assetProtocol の設定が別途必要です。

3. バックエンドから読む (Rust)

use tauri::{path::BaseDirectory, Manager};

#[tauri::command]
fn load_lang(app: tauri::AppHandle, code: String) -> Result<String, String> {
    let path = app
        .path()
        .resolve(format!("lang/{code}.json"), BaseDirectory::Resource)
        .map_err(|e| e.to_string())?;
    std::fs::read_to_string(&path).map_err(|e| e.to_string())
}

// 初期 DB はリソースから書き込み可能な場所へコピーしてから開く
#[tauri::command]
fn prepare_db(app: tauri::AppHandle) -> Result<String, String> {
    let src = app.path().resolve("db/seed.db", BaseDirectory::Resource).map_err(|e| e.to_string())?;
    let data_dir = app.path().app_data_dir().map_err(|e| e.to_string())?;
    std::fs::create_dir_all(&data_dir).map_err(|e| e.to_string())?;
    let dst = data_dir.join("app.db");
    if !dst.exists() {
        std::fs::copy(&src, &dst).map_err(|e| e.to_string())?;
    }
    Ok(dst.to_string_lossy().into_owned())
}

invoke_handler に tauri::generate_handler![load_lang, prepare_db] を登録し、フロントからは invoke('prepare_db') で呼びます。ディレクトリ自体は app.path().resource_dir() で得られます。

動作確認

npm run tauri dev では、リソースが src-tauri/target/debug/ 配下にコピーされ、そこが resource_dir() になります。tauri.conf.json を変更すると dev が再起動し、追加したファイルが読めるようになります。

$ npm run tauri dev
...
[load_lang] /home/you/app/src-tauri/target/debug/lang/ja.json

本番ビルド後は、Windows なら実行ファイルと同じフォルダ、macOS なら .app/Contents/Resources/、Linux の .deb なら /usr/lib/<name>/ に lang/ や db/ が展開されていることを確認します。

よくあるエラーと対処法

フロントから読むと not allowed になる

「fs.read_text_file not allowed. Permissions associated with this command: ...」という趣旨のエラーは、capability にリソースの読み取り権限がないためです。fs:allow-resource-read-recursive を追加してください。非再帰の fs:allow-resource-read でサブディレクトリを読もうとした場合も同じエラーになります。

ビルド時に glob に一致するファイルがない

resources/** のようにディレクトリだけに一致するパターンや、typo でファイルが見つからない場合、バンドル段階で「リソースが見つからない」という趣旨のエラーで止まります。ファイルを含めるには resources/**/* のように末尾に * を付けます。

インストール後に DB へ書き込めない

macOS の署名済み .app や Windows の Program Files、Linux の /usr/lib は一般ユーザーには書き込めません。リソースは読み取り専用と考え、上の prepare_db のように初回起動時に app_data_dir() へコピーしてから使ってください。

OS ごとの違いと注意点

  • Windows: リソースは実行ファイルの隣(インストールフォルダ直下)に展開されます。resource_dir() は .exe のあるディレクトリです
  • macOS: <productName>.app/Contents/Resources/ 配下。署名後にファイルを差し替えると署名が壊れます
  • Linux: .deb / .rpm は /usr/lib/<パッケージ名>/、AppImage は展開先の usr/lib/<パッケージ名>/ です
  • 実行可能なバイナリを同梱する場合は resources ではなく externalBin を使います(同梱したバイナリファイルを実行する)

関連レシピ

参考リンク(公式ドキュメント)

Web Ninja

この記事を書いた人

Web Ninja ウェブエンジニア (Web Engineer)

会社員ネットワークエンジニアから独立してかれこれ 25 年以上 Web エンジニアとして活動中。普段は JavaScript と Node.js を自在に操り、時には C++ や Perl といった古流の技も嗜みます。近年は Tauri × Rust という新たな武器を手に、デスクトップアプリ開発の最前線を駆け抜けています。「作りたい」を「作れる」に変えるための、実践的な「技」をお届けします。

お問い合わせ: tauri.ninja@gmail.com

内容の誤り・動かないコードを報告する