首页 / Tauri 2 入门教程 / 文件系统操作

Tauri 2 入门教程

文件系统操作

本教程共 48 篇 · 第 32 篇 · 更新于 2026-08-09 · 约 8 分钟阅读

TauriTauri 2 入门教程文件系统plugin-fsBaseDirectory作用域scope

本节目标:读完你能用前端 JS 读写、删除、监控文件,并理解 2.x 的 scope 是怎么限制「前端只能动哪些路径」的。

桌面应用离不开文件:存配置、记日志、导出导入数据……Tauri 把文件操作做成了官方插件 @tauri-apps/plugin-fs,让你在前端 JS 里就能读写文件,不用自己写 Rust 命令。但有个 2.x 特有的关键点:前端能碰的路径被「作用域(scope)」严格圈定,不是整个硬盘随便进。本章先把常用 API 讲完,再专门拆 scope。

安装与注册 fs 插件

和所有插件一样,两端都要接:

cd src-tauri
cargo add tauri-plugin-fs
// src-tauri/src/lib.rs
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
    tauri::Builder::default()
        .plugin(tauri_plugin_fs::init())
        .run(tauri::generate_context!())
        .expect("运行 Tauri 应用时出错");
}
npm install @tauri-apps/plugin-fs
Note

如果你要在 Rust 侧(而非前端)操作文件,官方建议直接用标准库 std::fstokio::fs,不需要走这个插件。前端插件只在「前端 JS 想直接碰文件」时才有用,而且必然受 scope 约束。

用 BaseDirectory 定位「家目录」

前端写文件最容易犯晕的是「路径写啥」。fs 插件提供 BaseDirectory(基准目录) 概念:你不写绝对路径,而是说「相对于某个系统目录」,比如应用数据目录、下载目录、家目录。这样既跨平台又不踩系统红线。

import { writeTextFile, BaseDirectory } from "@tauri-apps/plugin-fs";

// 把一段文本写到「应用数据目录」下的 config.json
await writeTextFile("config.json", '{"theme":"dark"}', {
    baseDir: BaseDirectory.AppData,
});

BaseDirectory.AppData 在不同系统指向不同位置(Windows 在 %APPDATA%,macOS 在 ~/Library/Application Support),但你代码里只写 AppData 就行,剩下的 Tauri 替你办。常用还有 AppConfigAppLocalDataHomeDownloadTemp 等。

写文件与读文件

写文本用 writeTextFile,读文本用 readTextFile

import {
    writeTextFile,
    readTextFile,
    BaseDirectory,
} from "@tauri-apps/plugin-fs";

// 写
await writeTextFile("notes.txt", "今天学会用 Tauri 读写文件", {
    baseDir: BaseDirectory.AppData,
});

// 读
const content = await readTextFile("notes.txt", {
    baseDir: BaseDirectory.AppData,
});
console.log(content); // 今天学会用 Tauri 读写文件

二进制文件则用 writeFile / readFile,传 Uint8Array。判断文件是否存在用 exists

import { exists, BaseDirectory } from "@tauri-apps/plugin-fs";

const has = await exists("notes.txt", { baseDir: BaseDirectory.AppData });
console.log("文件在吗:", has);
Tip

想要更细的控制(只读、追加、不存在才创建等),用 open(path, options) 拿到一个文件句柄,再 read / write,最后记得 close()。比如 { write: true, append: true } 就是「以追加模式打开」。普通一行读写用上面的 writeTextFile / readTextFile 就够了。

删除文件与目录

删除用 remove,文件目录通用,但删非空目录要显式开 recursive

import { remove, mkdir, BaseDirectory } from "@tauri-apps/plugin-fs";

// 建一个目录
await mkdir("images", { baseDir: BaseDirectory.AppData });

// 删一个文件
await remove("notes.txt", { baseDir: BaseDirectory.AppData });

// 删一个非空目录(必须 recursive: true)
await remove("images", {
    baseDir: BaseDirectory.AppData,
    recursive: true,
});

文件不存在时 remove 会抛错,删除前用 exists 先判断会更稳妥。

监控文件变化

想监听某个文件或目录是否被改动(比如日志在增长、配置被外部改了),用 watch

import { watch, BaseDirectory } from "@tauri-apps/plugin-fs";

// 监控应用日志目录,有变动就打印事件
await watch(
    "app.log",
    (event) => {
        console.log("文件变了:", event);
    },
    {
        baseDir: BaseDirectory.AppLog,
        delayMs: 500,   // 防抖:安静 500ms 后才触发
    }
);

watch 默认不递归子目录;要连子目录一起盯,加 recursive: true。还有个 watchImmediate,改动立刻通知、不做防抖,按需选用。

Warning

watch 依赖 Rust 端的 watch 特性开关。要在 Cargo.toml 里这样开,否则编译时找不到这个能力:

[dependencies]
tauri-plugin-fs = { version = "2", features = ["watch"] }

2.x 的作用域:scope 限制能访问的路径

前面的例子都没提「授权」,但默认竟然能跑——前提是你在 capability 里开了 fs:default。而 scope 真正生效的地方,是限制前端能碰哪些具体路径

fs 插件默认所有危险的读写命令和路径都是被拦的。你在 capability 里授权,本身就是「开一道门」;而 scope 决定「这道门只通到哪几个房间」。比如你允许读文件,但不能让前端读你整个硬盘,于是用 scope 把可读范围锁在 $APPDATA 里。

scope 写在 permission 的对象形式里,用 allow 列出允许的路径(glob 语法,** 表示递归子目录):

{
  "identifier": "fs:allow-read-file",
  "allow": [{ "path": "$APPDATA/**" }]
}

deny 优先级高于 allow:某路径即使被 allow,只要也进了 deny,运行时照样被挡。这适合「允许大部分、唯独排除某个敏感子目录」的场景。

capabilities 中授权 + scope 配置

一个完整、克制又安全的 fs 授权示例长这样,放在 src-tauri/capabilities/default.json

{
  "$schema": "../gen/schemas/desktop-schema.json",
  "identifier": "main-capability",
  "description": "主窗口的权限",
  "windows": ["main"],
  "permissions": [
    "core:default",
    "fs:default",
    {
      "identifier": "fs:allow-read-file",
      "allow": [{ "path": "$APPDATA/**" }]
    },
    {
      "identifier": "fs:allow-write-file",
      "allow": [{ "path": "$APPDATA/**" }]
    }
  ]
}

这里 fs:default 先开放一组常用命令,再额外用对象形式的 fs:allow-read-file / fs:allow-write-file 叠加 scope,把读写锁死在 $APPDATA 及其子目录。$APPDATA 这类变量是 Tauri 提供的路径占位符,对应各系统的应用数据目录;还有 $HOME$DOWNLOAD$TEMP 等,覆盖常见的系统目录。

如果你只想「整个插件按某个全局 scope 放开」,可以用 fs:scope 一次性圈定所有 fs 命令的允许范围:

{
  "identifier": "fs:scope",
  "allow": [
    { "path": "$APPDATA" },
    { "path": "$APPDATA/**" }
  ]
}
Tip

不确定路径占位符怎么写?Tauri 提供了一整套 $<名字> 变量(如 $APPCONFIG$APPLOCALDATA$DOCUMENT),它们对应 path API 里那些 BaseDirectory 的取值。在 capability 里写 scope 时用的变量,和前端 baseDir 用的枚举是一一对应的。

小结

@tauri-apps/plugin-fs 让你在前端直接读写删监控文件:writeTextFile / readTextFile 处理文本,remove 删除(非空目录要 recursive),watch 监控变化(需开 Rust 端 watch 特性),exists 判断存在。路径多用 BaseDirectory 相对系统目录定位,避免写死绝对路径。2.x 的关键约束是 scope:fs 默认全拒绝,必须在 capability 里授权;授权时可叠加 allow / deny 的 glob 路径,把前端能碰的范围锁死在具体目录(如 $APPDATA/**),既好用又安全。