TauriでマルチペインSFTPクライアントは作れるのか調べた
きっかけ
MacのSFTPクライアント、使いづらいソフトしかない。複数のディレクトリを別ペインで同時に開きたいのに、それができない。
同じサーバーでも /var/www/production/assets/2024/01/ と /var/www/staging/assets/ みたいに、深い階層の決まった場所を行き来することが多い。毎回ナビゲートするのがダルい。
「自分で作れ」「Tauriでいけるだろ」と言われたので、本当にできるのか調べてみた。
結論
Tauriで十分作れる。
- 既に実績あり(AeroFTP、Jexpe、T-Shellなど)
- RustのSFTPクレートも充実している
- macOSネイティブアプリとしてビルド可能
使えるRustクレート
| クレート | 特徴 |
|---|---|
| russh-sftp | Pure Rust、async/tokio対応。おすすめ |
| ssh2 | libssh2のラッパー。安定している |
| remotefs-ssh | 高レベルAPI。シンプルに使える |
russh-sftp が最も現代的で、Tauriのtokioベースのアーキテクチャと相性がいい。
設計思想
ローカルは既存ファイラーに任せる
このアプリはリモート専用。ローカル側のファイル操作はFinderやQSpaceに任せる。
- ローカルファイルシステムのブラウジング機能は不要
- FinderからのD&Dでアップロード
- アプリからFinderへのD&Dでダウンロード
シンプルな役割分担。
複数ペインで複数パス
同一サーバーで複数ペインを開ける。接続は共有してパスだけ別。
┌─────────────────────────────────────────┐
│ [+] 新規ペイン [接続設定] │
├──────────────┬──────────────┬───────────┤
│ サーバーA │ サーバーA │ サーバーB │
│ /var/www │ /home/user │ /opt/app │
├──────────────┼──────────────┼───────────┤
│ css/ │ .bashrc │ logs/ │
│ js/ │ .zshrc │ conf/ │
│ index.html │ │ │
└──────────────┴──────────────┴───────────┘
ペイン間のD&Dで、同一サーバー内ならローカルを経由せず直接コピー/移動もできるはず。
技術的なポイント
Tauri IPC
#[tauri::command] でRust関数をフロントエンドに公開する。
#[tauri::command(async)]
async fn list_directory(
state: State<'_, SftpManager>,
pane_id: String,
path: String,
) -> Result<Vec<FileEntry>, String> {
// ディレクトリ一覧を取得
}
フロントエンドからは invoke で呼び出す。
const files = await invoke('list_directory', {
paneId: 'left',
path: '/var/www'
});
D&D対応
Tauriは標準でファイルドロップをサポートしている。
tauri::Builder::default()
.on_file_drop_event(|event| {
if let tauri::FileDropEvent::Dropped(paths) = event.event {
// Finderからドロップされたファイルパス
}
})
状態管理
ペインごとのSFTPセッションを HashMap で管理する。
struct SftpManager {
sessions: HashMap<String, SftpSession>, // pane_id → session
}
同一サーバーへの複数ペインなら、SSH接続は1本でSFTPチャネルだけ複数開く設計もできる。
非同期処理
russh-sftp はtokioベースなので、大量のファイルリストやファイル転送でもUIがブロックされない。進捗表示も window.emit() でリアルタイムに更新できる。
注意点
IPCのオーバーヘッド
TauriのIPCはJSONシリアライズ。ファイル数が多いとオーバーヘッドになる。
対策: ページネーションで50〜100件ずつ取得する。
macOSサンドボックス
App Sandboxを有効にする場合、ネットワーク権限の設定が必要。Tauriがほぼ自動で設定してくれるが、Entitlements.plist の確認は必要。
SSH鍵認証
- ssh-agent連携:
ssh2::Agentでシステムのssh-agentを使える - 鍵ファイル直接指定:
ssh_keyクレートでPEM/OpenSSH形式を読める
開発期間の目安
- Rust経験者: 2〜4週間でコア機能
- Rust学習しながら: 4〜8週間
フロントエンドはReactでもSvelteでも好みで。
参考になりそうなプロジェクト
- Jexpe - Tauri + Next.jsのSSH/SFTPクライアント
- russh examples - SFTPクライアントの実装例
まとめ
TauriでマルチペインのSFTPクライアント、技術的には十分実現可能。既存の実装例もあるし、クレートも揃っている。
時間ができたら作ってみたい。
CyberDuckが複数開けるだけでもまあいいんだが……