在 Tauri 中,文件系统操作并非像传统 Node.js 那样可以任意调用,而是严格遵循“默认拒绝、按需授权”的安全策略。所有文件读写和目录操作都必须通过 Rust 后端执行,前端通过 IPC 命令触发,并在配置中显式声明允许访问的路径或范围。这种设计既保证了功能的可用性,又避免了恶意网页利用漏洞访问用户敏感文件。
权限控制:安全的第一道关卡
在 tauri.conf.json 中,你需要定义一个或多个权限(permission),并将其分配给相应的命令或前端窗口。通常使用 Tauri 内置的文件系统插件(@tauri-apps/plugin-fs)来实现读写操作,该插件已经封装了大部分安全检查和跨平台兼容性。
一个典型的权限配置如下(tauri.conf.json 中的 plugins.fs 部分):
{
"plugins": {
"fs": {
"scope": {
"allow": [
{
"path": "$APPDATA/my-app/**",
"read": true,
"write": true
},
{
"path": "$HOME/Documents/**",
"read": true
}
],
"deny": [
{
"path": "$HOME/Documents/passwords.txt"
}
]
}
}
}
}
关键点:
- 路径前缀:使用如
$APPDATA、$HOME、$RESOURCE等变量指定作用域,避免硬编码绝对路径,提高跨平台兼容性。 - 读写分离:可以仅授予“只读”权限,而不能写入,满足最小权限原则。
- 精细化与拒绝列表:允许某目录的同时,可以单独禁止特定文件或子目录。
- 前端不可直接绕过:即使用户的前端代码被注入恶意脚本,攻击者也只能在你预先声明的范围内读写文件,无法访问任意位置。
文件读写:像操作普通 API 一样简单
在前端,安装插件后可以直接调用 @tauri-apps/plugin-fs 提供的函数。这些函数都是异步的,返回 Promise,不会阻塞 UI。
读取文本文件:
import { readTextFile } from '@tauri-apps/plugin-fs';
const content = await readTextFile('settings.json'); // 路径相对于声明的 scope
如果文件不存在,会抛出异常,可以用 try/catch 处理。
写入字符串:
import { writeTextFile } from '@tauri-apps/plugin-fs';
await writeTextFile('settings.json', JSON.stringify({ theme: 'dark' }));
二进制文件读写:
使用 readFile 和 writeFile,返回和接收 Uint8Array,适合图像、PDF 等。
目录操作:创建、枚举、删除
同样需要提前声明路径权限。常用的目录操作包括:
创建目录:
import { mkdir } from '@tauri-apps/plugin-fs';
await mkdir('screenshots', { recursive: true }); // 递归创建
列出目录内容:
import { readDir } from '@tauri-apps/plugin-fs';
const entries = await readDir('screenshots');
for (const entry of entries) {
console.log(entry.name, entry.isDirectory, entry.isFile);
}
删除文件或目录:
import { remove, removeDir } from '@tauri-apps/plugin-fs';
await remove('temp.txt'); // 删除文件
await removeDir('backups', { recursive: true }); // 删除整个目录
检查路径存在性与元数据:
import { exists, stat } from '@tauri-apps/plugin-fs';
if (await exists('config.json')) {
const meta = await stat('config.json');
console.log(meta.size, meta.mtime);
}
实际开发中的最佳实践
- 尽量把数据写在应用专属目录:使用
$APPDATA/my-app或 Tauri 提供的appDataDir()API 获取路径,不要随意向$HOME根目录写入文件。 - 将复杂逻辑放在 Rust 后端:如果需要加密解密、处理大文件流、数据备份等,建议用 Rust 命令实现,前端只负责发起触发。这样可以利用 Rust 的性能,并且减少前端代码对文件系统的直接依赖。
- 测试权限边界:在开发阶段故意用前端调用未允许的路径,观察应用是否能正确返回权限拒绝错误(不应该崩溃),保证沙箱机制有效。
- 跨平台路径处理:权限配置中的变量(如
$APPDATA)会在运行时自动映射到对应平台的路径(Windows 上是C:\Users\<user>\AppData\Roaming,macOS 上是~/Library/Application Support),避免你自己做平台判断。
借助 Tauri 的文件系统插件,你可以用熟悉的 Promise 语法实现可靠的文件管理,而底层的权限模型和 Rust 的隔离执行则确保了用户数据的安全。这种“上层易用、底层牢固”的设计,正是 Tauri 在处理本地资源时的核心优势。