文件系统 #

一、文件系统概述 #

1.1 文件系统架构 #

text
┌─────────────────────────────────────────────────────────────┐
│                      文件系统架构                            │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  ┌─────────────────────────────────────────────────────┐   │
│  │                    前端 API                          │   │
│  │  @tauri-apps/plugin-fs                              │   │
│  └─────────────────────────────────────────────────────┘   │
│                            │                                │
│                            │ IPC                            │
│                            ▼                                │
│  ┌─────────────────────────────────────────────────────┐   │
│  │                    Rust 后端                         │   │
│  │  std::fs / tokio::fs                                │   │
│  └─────────────────────────────────────────────────────┘   │
│                            │                                │
│                            ▼                                │
│  ┌─────────────────────────────────────────────────────┐   │
│  │                    操作系统                          │   │
│  │  Windows / macOS / Linux                            │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
└─────────────────────────────────────────────────────────────┘

1.2 权限配置 #

json
// src-tauri/capabilities/default.json
{
    "permissions": [
        "fs:allow-read-text-file",
        "fs:allow-write-text-file",
        "fs:allow-read-dir",
        "fs:allow-exists",
        "fs:allow-mkdir",
        "fs:allow-remove",
        "fs:allow-rename"
    ]
}

二、安装插件 #

2.1 安装依赖 #

bash
pnpm add @tauri-apps/plugin-fs

2.2 注册插件 #

rust
// src-tauri/src/lib.rs
tauri::Builder::default()
    .plugin(tauri_plugin_fs::init())
    .run(tauri::generate_context!())
    .expect("error while running tauri application");

三、文件读写 #

3.1 读取文本文件 #

typescript
import { readTextFile } from '@tauri-apps/plugin-fs';

async function readFile(path: string): Promise<string> {
    try {
        const content = await readTextFile(path);
        return content;
    } catch (error) {
        console.error('Failed to read file:', error);
        throw error;
    }
}

3.2 写入文本文件 #

typescript
import { writeTextFile } from '@tauri-apps/plugin-fs';

async function writeFile(path: string, content: string): Promise<void> {
    try {
        await writeTextFile(path, content);
    } catch (error) {
        console.error('Failed to write file:', error);
        throw error;
    }
}

3.3 读取二进制文件 #

typescript
import { readFile } from '@tauri-apps/plugin-fs';

async function readBinaryFile(path: string): Promise<Uint8Array> {
    const data = await readFile(path);
    return data;
}

3.4 写入二进制文件 #

typescript
import { writeFile } from '@tauri-apps/plugin-fs';

async function writeBinaryFile(path: string, data: Uint8Array): Promise<void> {
    await writeFile(path, data);
}

3.5 追加内容 #

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

async function appendToFile(path: string, content: string): Promise<void> {
    await writeTextFile(path, content, {
        append: true,
    });
}

四、目录操作 #

4.1 创建目录 #

typescript
import { mkdir } from '@tauri-apps/plugin-fs';

async function createDirectory(path: string): Promise<void> {
    await mkdir(path, {
        recursive: true,
    });
}

4.2 读取目录 #

typescript
import { readDir } from '@tauri-apps/plugin-fs';

interface DirEntry {
    name: string;
    isDirectory: boolean;
    isFile: boolean;
    isSymlink: boolean;
}

async function listDirectory(path: string): Promise<DirEntry[]> {
    const entries = await readDir(path);
    return entries.map(entry => ({
        name: entry.name,
        isDirectory: entry.isDirectory,
        isFile: entry.isFile,
        isSymlink: entry.isSymlink,
    }));
}

4.3 删除目录 #

typescript
import { remove } from '@tauri-apps/plugin-fs';

async function removeDirectory(path: string): Promise<void> {
    await remove(path, {
        recursive: true,
    });
}

五、文件操作 #

5.1 检查文件是否存在 #

typescript
import { exists } from '@tauri-apps/plugin-fs';

async function fileExists(path: string): Promise<boolean> {
    return await exists(path);
}

5.2 复制文件 #

typescript
import { copyFile } from '@tauri-apps/plugin-fs';

async function copy(source: string, destination: string): Promise<void> {
    await copyFile(source, destination);
}

5.3 重命名文件 #

typescript
import { rename } from '@tauri-apps/plugin-fs';

async function renameFile(oldPath: string, newPath: string): Promise<void> {
    await rename(oldPath, newPath);
}

5.4 删除文件 #

typescript
import { remove } from '@tauri-apps/plugin-fs';

async function deleteFile(path: string): Promise<void> {
    await remove(path);
}

5.5 获取文件元数据 #

typescript
import { stat } from '@tauri-apps/plugin-fs';

interface FileMetadata {
    isFile: boolean;
    isDirectory: boolean;
    isSymlink: boolean;
    size: number;
    modifiedAt: Date | null;
    accessedAt: Date | null;
    createdAt: Date | null;
}

async function getMetadata(path: string): Promise<FileMetadata> {
    const metadata = await stat(path);
    return {
        isFile: metadata.isFile,
        isDirectory: metadata.isDirectory,
        isSymlink: metadata.isSymlink,
        size: metadata.size,
        modifiedAt: metadata.mtime ? new Date(metadata.mtime) : null,
        accessedAt: metadata.atime ? new Date(metadata.atime) : null,
        createdAt: metadata.birthtime ? new Date(metadata.birthtime) : null,
    };
}

六、特殊目录 #

6.1 获取应用目录 #

typescript
import {
    appConfigDir,
    appDataDir,
    appCacheDir,
    appLogDir,
    desktopDir,
    documentDir,
    downloadDir,
    homeDir,
    pictureDir,
    musicDir,
    videoDir,
} from '@tauri-apps/api/path';

async function getDirectories() {
    return {
        config: await appConfigDir(),
        data: await appDataDir(),
        cache: await appCacheDir(),
        log: await appLogDir(),
        desktop: await desktopDir(),
        documents: await documentDir(),
        downloads: await downloadDir(),
        home: await homeDir(),
        pictures: await pictureDir(),
        music: await musicDir(),
        videos: await videoDir(),
    };
}

6.2 使用基础目录 #

typescript
import { writeTextFile } from '@tauri-apps/plugin-fs';
import { appDataDir, join } from '@tauri-apps/api/path';

async function saveToAppData(filename: string, content: string) {
    const appData = await appDataDir();
    const filePath = await join(appData, filename);
    await writeTextFile(filePath, content);
}

七、文件监听 #

7.1 监听文件变更 #

typescript
import { watch, WatchEvent } from '@tauri-apps/plugin-fs';

async function watchFile(path: string, callback: (event: WatchEvent) => void) {
    const unwatch = await watch(path, (event) => {
        callback(event);
    });

    return unwatch;
}

// 使用示例
const unwatch = await watchFile('/path/to/file', (event) => {
    console.log('File changed:', event);
    if (event.type === 'modify') {
        console.log('File modified');
    }
});

// 停止监听
unwatch();

7.2 监听目录 #

typescript
import { watch } from '@tauri-apps/plugin-fs';

async function watchDirectory(path: string) {
    const unwatch = await watch(
        path,
        (event) => {
            console.log('Directory event:', event);
        },
        { recursive: true }
    );

    return unwatch;
}

八、后端文件操作 #

8.1 Rust 文件命令 #

rust
use std::fs;
use std::path::PathBuf;

#[tauri::command]
fn read_file_content(path: String) -> Result<String, String> {
    fs::read_to_string(&path)
        .map_err(|e| format!("Failed to read file: {}", e))
}

#[tauri::command]
fn write_file_content(path: String, content: String) -> Result<(), String> {
    fs::write(&path, content)
        .map_err(|e| format!("Failed to write file: {}", e))
}

#[tauri::command]
fn list_files(dir: String) -> Result<Vec<String>, String> {
    let entries = fs::read_dir(&dir)
        .map_err(|e| format!("Failed to read directory: {}", e))?;
    
    let files: Vec<String> = entries
        .filter_map(|entry| entry.ok())
        .map(|entry| entry.path().to_string_lossy().to_string())
        .collect();
    
    Ok(files)
}

8.2 异步文件操作 #

rust
use tokio::fs;

#[tauri::command]
async fn read_file_async(path: String) -> Result<String, String> {
    fs::read_to_string(&path)
        .await
        .map_err(|e| format!("Failed to read file: {}", e))
}

#[tauri::command]
async fn write_file_async(path: String, content: String) -> Result<(), String> {
    fs::write(&path, content)
        .await
        .map_err(|e| format!("Failed to write file: {}", e))
}

九、文件工具类 #

9.1 文件管理器 #

typescript
// utils/fileManager.ts
import {
    readTextFile,
    writeTextFile,
    exists,
    mkdir,
    remove,
    rename,
    copyFile,
    readDir,
} from '@tauri-apps/plugin-fs';
import { join, dirname } from '@tauri-apps/api/path';

export class FileManager {
    static async read(path: string): Promise<string> {
        return readTextFile(path);
    }

    static async write(path: string, content: string): Promise<void> {
        const dir = await dirname(path);
        if (!(await exists(dir))) {
            await mkdir(dir, { recursive: true });
        }
        await writeTextFile(path, content);
    }

    static async delete(path: string): Promise<void> {
        if (await exists(path)) {
            await remove(path, { recursive: true });
        }
    }

    static async move(source: string, destination: string): Promise<void> {
        await rename(source, destination);
    }

    static async copy(source: string, destination: string): Promise<void> {
        await copyFile(source, destination);
    }

    static async list(path: string): Promise<string[]> {
        const entries = await readDir(path);
        return entries.map((e) => e.name);
    }

    static async exists(path: string): Promise<boolean> {
        return exists(path);
    }
}

9.2 配置文件管理 #

typescript
// utils/configManager.ts
import { FileManager } from './fileManager';
import { appConfigDir, join } from '@tauri-apps/api/path';

interface AppConfig {
    theme: string;
    language: string;
    fontSize: number;
}

export class ConfigManager {
    private static configPath: string | null = null;

    private static async getConfigPath(): Promise<string> {
        if (!this.configPath) {
            const configDir = await appConfigDir();
            this.configPath = await join(configDir, 'config.json');
        }
        return this.configPath;
    }

    static async load(): Promise<AppConfig> {
        const path = await this.getConfigPath();
        
        if (!(await FileManager.exists(path))) {
            return this.getDefaultConfig();
        }

        const content = await FileManager.read(path);
        return JSON.parse(content);
    }

    static async save(config: AppConfig): Promise<void> {
        const path = await this.getConfigPath();
        await FileManager.write(path, JSON.stringify(config, null, 2));
    }

    private static getDefaultConfig(): AppConfig {
        return {
            theme: 'light',
            language: 'en',
            fontSize: 14,
        };
    }
}

十、最佳实践 #

10.1 错误处理 #

typescript
async function safeReadFile(path: string): Promise<string | null> {
    try {
        return await readTextFile(path);
    } catch (error) {
        console.error(`Failed to read file ${path}:`, error);
        return null;
    }
}

10.2 路径处理 #

typescript
import { join, resolve, basename, dirname, extname } from '@tauri-apps/api/path';

async function processPath(filePath: string) {
    const absolute = await resolve(filePath);
    const name = await basename(filePath);
    const dir = await dirname(filePath);
    const ext = await extname(filePath);
    
    console.log({ absolute, name, dir, ext });
}

10.3 大文件处理 #

typescript
// 分块读取大文件
async function readLargeFile(path: string, chunkSize: number = 1024 * 1024) {
    const file = await readFile(path);
    const chunks: Uint8Array[] = [];
    
    for (let i = 0; i < file.length; i += chunkSize) {
        const chunk = file.slice(i, i + chunkSize);
        chunks.push(chunk);
    }
    
    return chunks;
}

十一、总结 #

11.1 核心要点 #

要点 说明
文件读写 使用 fs 插件
目录操作 mkdir、readDir、remove
特殊目录 使用 path API
文件监听 watch API
后端操作 Rust std::fs

11.2 下一步 #

现在你已经掌握了文件系统操作,接下来让我们学习 数据库集成,了解如何在 Tauri 中使用数据库!

最后更新:2026-03-28