如果你正在开发一个桌面应用,迟早会遇到需要把数据保存到本地的场景。比如用户配置、日志记录、缓存文件等等。Tauri作为一个轻量级的桌面应用框架,提供了FS(文件系统)模块来帮你搞定这些需求。
我第一次用Tauri FS模块时,以为就是个简单的读写文件接口,结果踩了不少坑。比如路径拼接总是出错,权限配置搞了半天不生效,还有不同操作系统下的路径差异问题。后来才发现,只要掌握几个关键点,这个模块用起来其实非常顺手。
Tauri FS模块的核心价值在于:
在tauri.conf.json文件中,fs模块的权限控制是重中之重。我见过不少开发者直接照搬官方示例,结果发现文件操作总是失败。关键在于理解scope和各个API开关的关系。
json复制"fs": {
"scope": ["$APPDATA/*"],
"all": true,
"readFile": true,
"writeFile": true
}
这里有几个容易出错的点:
all: true看似方便,但生产环境建议按需开启具体API*通配符只匹配当前目录,**才能匹配子目录Tauri提供了多种BaseDirectory选项,但文档没说清楚它们在各个系统的实际位置。经过实测,整理出这张对照表:
| BaseDirectory | Windows路径示例 | macOS路径示例 |
|---|---|---|
| AppData | C:\Users\admin\AppData\Roaming | ~/Library/Application Support |
| Cache | C:\Users\admin\AppData\Local | ~/Library/Caches |
| Desktop | C:\Users\admin\Desktop | ~/Desktop |
特别注意:路径最后会自动加上你的应用标识符(tauri.conf.json中的identifier值),比如com.tauri.build会生成.../Roaming/com.tauri.build/这样的目录。
第一次用writeTextFile时,我被路径拼接坑惨了。看这段典型错误代码:
javascript复制// 错误示例:路径拼接问题
writeTextFile("logs" + "\error.log", "content", {
dir: BaseDirectory.AppData
})
问题出在:
\\\需要转义为\\"logs\\\\error.log"正确的做法是使用path模块:
javascript复制import { join } from '@tauri-apps/api/path'
const filePath = await join('logs', 'error.log')
await writeTextFile(filePath, 'content', {
dir: BaseDirectory.AppData
})
创建目录时,recursive参数非常有用:
javascript复制await createDir('user/data/cache', {
dir: BaseDirectory.AppData,
recursive: true // 自动创建所有父目录
})
遍历目录时,注意entries的结构:
javascript复制const entries = await readDir('user', {
dir: BaseDirectory.AppData,
recursive: true
})
entries.forEach(entry => {
console.log(entry.name) // 文件名
console.log(entry.path) // 完整路径
console.log(entry.children) // 子目录项
})
当处理大文件时,直接readFile可能会内存溢出。建议使用流式处理:
javascript复制import { readBinaryFileChunked } from '@tauri-apps/api/fs'
const chunkSize = 1024 * 1024 // 1MB
let offset = 0
let chunks = []
while(true) {
const { chunk, endOfFile } = await readBinaryFileChunked('bigfile.dat', {
dir: BaseDirectory.AppData,
offset,
chunkSize
})
chunks.push(chunk)
offset += chunkSize
if(endOfFile) break
}
虽然Tauri没有内置文件监控,但可以用setInterval实现简单轮询:
javascript复制let lastModified = 0
setInterval(async () => {
const { modified } = await getFileInfo('config.json', {
dir: BaseDirectory.AppData
})
if(modified > lastModified) {
lastModified = modified
console.log('文件已更新')
}
}, 1000)
当文件操作失败时,首先确认完整路径:
javascript复制import { resolve } from '@tauri-apps/api/path'
const fullPath = await resolve('config.json', {
dir: BaseDirectory.AppData
})
console.log(fullPath) // 打印完整路径检查是否正确
如果遇到权限错误,检查三个地方:
处理路径时永远不要硬编码分隔符。比如这段代码在Windows和macOS都能运行:
javascript复制import { sep } from '@tauri-apps/api/path'
const path = `data${sep}cache${sep}temp.dat`
最后分享一个完整的用户配置管理实现:
javascript复制import {
exists,
readTextFile,
writeTextFile,
createDir
} from '@tauri-apps/api/fs'
import { join } from '@tauri-apps/api/path'
async function loadConfig() {
const configDir = await join('myapp', 'config')
const configFile = await join(configDir, 'settings.json')
if(!await exists(configFile)) {
await createDir(configDir, {
dir: BaseDirectory.AppData,
recursive: true
})
await writeTextFile(configFile, '{}', {
dir: BaseDirectory.AppData
})
return {}
}
const content = await readTextFile(configFile, {
dir: BaseDirectory.AppData
})
return JSON.parse(content)
}
async function saveConfig(config) {
const configFile = await join('myapp', 'config', 'settings.json')
await writeTextFile(configFile, JSON.stringify(config), {
dir: BaseDirectory.AppData
})
}
这个方案解决了几个关键问题: