TAURIとは
マルチプラットフォームのGUIフレームワークで、
フロントエンドをHTML+JavaScriptで、バックエンドをRustでそれぞれ記述できます。
Electronのようなものと表現すればよいでしょうか。
ローカルファイルを取り扱いたい
GUIのスタンドアロンアプリであることがWebアプリと最も違うのは、ローカルファイルへのアクセスです。
一方フロントエンドのHTML/JSでローカルファイルを取り扱うのが難しいことも考えられます。
本記事では以下のような手順を考えます。
- メニューから「ファイルを開く」を選択してダイアログを開く。
- ダイアログからファイルを選択する。
- 選択されたファイルをフロントエンド側で読み込む。
この場合、以下の2パターンが考えられます。
- バックエンド側でメニュー選択からファイルダイアログを経由してファイルパスを取得->フロントエンドに通知する。
- バックエンド側のメニューが選択されたことを->フロントエンド側からファイルダイアログを経由してファイルパスを取得する。
これは、メニュー制御はバックエンド側の担当となるためです。
バックエンドとフロントエンド間の通信も考慮する必要があります。
メニューの取扱については公式のガイドを参照してください。
フロントエンド<->バックエンド間の通信はeventを利用するのがいいでしょう。
こちらも公式ガイドが用意されています。
記事執筆時点では、詳細なチュートリアルが公式になかった以下の2つ
- ダイアログの開き方
- フロントエンドからローカルファイルに直接アクセスするための手順
を中心に取り扱います。
なお、Windows10ネイティブ環境で動作確認を行っています。
ダイアログを開くための設定
tauri.conf.json
のallowlist
に設定が必要です。
"tauri": {
"allowlist": {
"all": true,
}
}
となっていればそのまま開くことができます。個別設定の場合は以下のようにします。
"tauri": {
"allowlist": {
"dialog": {
"all": true,
"open": true,
"save": true
}
}
}
ちなみに…
-
all: true
かdialog
のどちらかを設定していないと、Rust側でダイアログ関連の処理を入れているとコンパイルそのものに失敗します。 -
dialog/all: false
の場合ダイアログを開けません。 -
dialog/all: false
でもdialog/open: true
の場合ファイルを開くダイアログは開けます(メッセージは開けません)。 -
dialog/all: true
の場合dialog/open: false
としてもファイルを開くダイアログは開けます(all優先?)。
ダイアログの開き方(Rust)
tauri::api::dialog
を使用します。
(ちなみに、tauri_api::dialog
とtauri_dialog
というのも存在していますが、
どちらもtauri_dialog
を利用しており、既にメンテナンスされていない旨ドキュメントに記述されています。)
メッセージダイアログの開き方
メッセージダイアログを開くための関数が用意されています。
利用方法はドキュメントに詳しく書いてあります。
ファイルダイアログの開き方
FileDialogBuilder
を経由して開くことができます。
non-blockingになっていて、
ダイアログを開いている間も元のウィンドウに戻って作業することができます。
use tauri::api::dialog::FileDialogBuilder;
// non-blockingの例
FileDialogBuilder::new().pick_files(move |file_paths| {
match file_paths {
Some(v) => {
let mut pathstr = "".to_string();
// 選択されたファイルのうち最初のファイルだけを対象とする
match v.first() {
Some(path) => {
match path.to_str() {
Some(s) => {
println!("{:?}", s);
pathstr = s.to_string();
}
_ => {}
}
}
_ => {}
}
// 親ウィンドウに対してファイルパスをイベントで通知
let window = event.window();
window.emit("pick_files", Payload {
file_paths: pathstr
});
}
_ => {}
}
})
一方、blockingも用意されています。
この場合はダイアログを開くとメインウィンドウの処理が止まります。
use tauri::api::dialog::blocking::FileDialogBuilder;
// blockingの例
let file_paths = FileDialogBuilder::new().pick_files();
match file_paths {
Some(v) => {
let mut pathstr = "".to_string();
// 選択されたファイルのうち最初のファイルだけを対象とする
match v.first() {
Some(path) => {
match path.to_str() {
Some(s) => {
println!("{:?}", s);
pathstr = s.to_string();
}
_ => {}
}
}
_ => {}
}
// 親ウィンドウに対してファイルパスをイベントで通知
let window = event.window();
window.emit("pick_files", Payload {
file_paths: pathstr
});
}
_ => {}
}
どちらの場合であっても、取得できるのはファイルパスであることに注意してください。
この後の処理は以下のような方法が考えられます。
- このままバックエンド側で処理し、eventを経由してフロントエンドに通知する。
- フロントエンドにeventを経由してファイルパスを通知し、フロントエンドから直接アクセスする。
どちらにしても、バックエンドからフロントエンドにeventを経由して通知を行う必要があります。
ダイアログの開き方(JavaScript)
こちらはapiドキュメントに詳しい使い方が書いてあるのでそちらを参照してください。
こちらを利用した場合はブロックされ、ダイアログを開いているとウィンドウ自体にフォーカスできなくなります。
フロントエンドからローカルファイルに直接アクセスする方法(HTML,JavaScript)
動画ファイルなど、フロントエンドから直接アクセスしたほうが取り扱いやすい場合もあります。
この場合tauri.conf.json
に次の2つの設定がまず必要になります。
-
security/csp
にasset.localhost
に対する許可の記述を追加 -
allowlist/protocol
にasset
関連の記述を追加
その上で、convertFileSrc
という関数を経由してURIに変換します。
APIドキュメントは断片的にしか書かれていないため、外部サイトの記述も参考にしています。
CSP(Content Security Policy)
CSPに関する詳細は例えば以下のページを参考にしてください。
ローカルファイルへアクセスするためには以下の記述が必要です。
"csp": "default-src 'self'; img-src 'self' asset: https://asset.localhost"
なお、この記述がないか間違っている場合はcsp errorというエラーが返ってきます。F12でデバッグコンソールを見るとわかります。
protocolの追加
cspを設定しただけだとローカルファイルのURIにアクセスしても403エラーが返ってきます。
もう一箇所、allowlist
にassetプロトコルの記述を追加します。
"tauri": {
"allowlist": {
"all": true,
"protocol": {
"asset": true,
"assetScope": ["**"]
}
}
}
これでローカルファイルへの直接アクセスができます。
なお、convertFileSrc
で変換されたURIは
https://asset.localhost/<ローカルファイルパス>
のようになります。
参考文献