3. SPIFFS 文件系统基础:挂载、格式化、文件读写操作
SPIFFS,全称是 SPI Flash File System。说白了,它就是 ESP32 内部 Flash 上的一块“小硬盘”。
我刚开始接触 ESP32 时,总觉得存个网页、配个文件很麻烦。后来发现 SPIFFS 这东西,其实就是帮我们把 Flash 的一部分划出来,当成一个微型文件系统用。你想想看,一个嵌入式设备能像电脑一样读写文件,是不是很爽?
核心概念:SPIFFS 是一个轻量级的文件系统,专门为 SPI Flash 芯片设计。它支持目录结构(虽然是扁平的),支持标准的文件操作(fopen、fclose、fread、fwrite)。
3.1 SPIFFS 的挂载与格式化
挂载,就是把文件系统“接”到系统里。就像你插 U 盘,系统得先识别它才能用。
在 ESP32 上,挂载 SPIFFS 需要调用 esp_vfs_spiffs_register() 函数。我个人习惯在 setup() 里就把它挂好,省得后面手忙脚乱。
#include <esp_spiffs.h>
void setup() {
Serial.begin(115200);
esp_vfs_spiffs_conf_t conf = {
.base_path = "/spiffs", // 挂载路径
.partition_label = NULL, // 使用默认分区
.max_files = 5, // 最大同时打开文件数
.format_if_mount_failed = true // 挂载失败自动格式化
};
esp_err_t ret = esp_vfs_spiffs_register(&conf);
if (ret != ESP_OK) {
if (ret == ESP_FAIL) {
Serial.println("挂载失败,已尝试格式化");
} else {
Serial.println("SPIFFS 初始化失败");
}
return;
}
Serial.println("SPIFFS 挂载成功!");
}
这里有个关键参数:format_if_mount_failed。我建议你把它设为 true。为什么?因为新芯片第一次用,Flash 里是空的,不格式化根本挂不上。我曾经踩过这个坑,折腾了半天才发现是没格式化。
小技巧:如果你不想每次挂载失败都格式化(比如怕丢数据),可以先用 esp_spiffs_info() 检查文件系统状态,再决定是否格式化。
3.2 文件读写操作:fopen / fclose
挂载好了,接下来就是读写文件。ESP32 的 SPIFFS 支持标准的 C 库文件操作函数。说白了,就是 fopen、fclose、fprintf、fread 这些老朋友。
我写代码时,习惯把文件操作封装成几个小函数。这样主程序看起来清爽,调试也方便。
3.2.1 写文件
void writeFile(const char *path, const char *content) {
FILE *file = fopen(path, "w");
if (file == NULL) {
Serial.println("打开文件失败");
return;
}
fprintf(file, "%s", content);
fclose(file);
Serial.println("文件写入成功");
}
注意:fopen 的第二个参数是模式字符串。常用的有:
| 模式 | 含义 | 注意事项 |
|---|---|---|
"w" |
写入模式,覆盖原文件 | 文件不存在会创建 |
"a" |
追加模式,在文件末尾添加 | 文件不存在会创建 |
"r" |
只读模式 | 文件不存在返回 NULL |
"r+" |
读写模式 | 文件必须存在 |
3.2.2 读文件
void readFile(const char *path) {
FILE *file = fopen(path, "r");
if (file == NULL) {
Serial.println("文件不存在");
return;
}
char line[128];
while (fgets(line, sizeof(line), file) != NULL) {
Serial.print(line);
}
fclose(file);
}
这里我用了 fgets 逐行读取。如果你要读二进制文件,可以用 fread。我个人更推荐 fgets 处理文本,因为它自带换行处理,省事。
避坑指南:SPIFFS 不支持目录嵌套!所有文件都在根目录下。我曾经试图创建 /spiffs/data/config.txt,结果死活打不开。后来才发现,SPIFFS 是扁平结构,只能 /spiffs/config.txt。
3.3 文件系统信息查询
有时候你需要知道 SPIFFS 还剩多少空间。比如,你要存一张大图片,得先看看够不够。
void printSpiffsInfo() {
size_t total = 0;
size_t used = 0;
esp_err_t ret = esp_spiffs_info(NULL, &total, &used);
if (ret != ESP_OK) {
Serial.println("获取 SPIFFS 信息失败");
return;
}
Serial.printf("总空间: %d bytes\n", total);
Serial.printf("已用空间: %d bytes\n", used);
Serial.printf("剩余空间: %d bytes\n", total - used);
}
嗯,这里要注意:esp_spiffs_info 的第一个参数是分区标签。如果你用的是默认分区,传 NULL 就行。
3.4 文件删除与重命名
删除文件用 remove(),重命名用 rename()。这两个函数都是标准 C 库的,用起来很简单。
void deleteFile(const char *path) {
if (remove(path) == 0) {
Serial.println("文件删除成功");
} else {
Serial.println("文件删除失败");
}
}
void renameFile(const char *oldPath, const char *newPath) {
if (rename(oldPath, newPath) == 0) {
Serial.println("文件重命名成功");
} else {
Serial.println("文件重命名失败");
}
}
小技巧:如果你要批量删除文件,可以用 opendir() 和 readdir() 遍历目录。不过 SPIFFS 文件数不多,直接循环删除也行。
3.5 完整示例:一个简单的文件管理器
我把上面这些操作整合成一个简单的示例。你可以在串口输入命令,实现文件读写、删除、查看信息等功能。
void setup() {
Serial.begin(115200);
// 挂载 SPIFFS
esp_vfs_spiffs_conf_t conf = {
.base_path = "/spiffs",
.partition_label = NULL,
.max_files = 5,
.format_if_mount_failed = true
};
if (esp_vfs_spiffs_register(&conf) != ESP_OK) {
Serial.println("SPIFFS 挂载失败");
return;
}
Serial.println("SPIFFS 就绪,输入命令:");
Serial.println(" write <文件名> <内容>");
Serial.println(" read <文件名>");
Serial.println(" delete <文件名>");
Serial.println(" info");
}
void loop() {
if (Serial.available()) {
String cmd = Serial.readStringUntil('\n');
cmd.trim();
if (cmd.startsWith("write ")) {
// 解析命令
int space1 = cmd.indexOf(' ', 6);
String path = cmd.substring(6, space1);
String content = cmd.substring(space1 + 1);
writeFile(("/spiffs/" + path).c_str(), content.c_str());
}
else if (cmd.startsWith("read ")) {
String path = cmd.substring(5);
readFile(("/spiffs/" + path).c_str());
}
else if (cmd.startsWith("delete ")) {
String path = cmd.substring(7);
deleteFile(("/spiffs/" + path).c_str());
}
else if (cmd == "info") {
printSpiffsInfo();
}
}
}
这个示例虽然简单,但已经涵盖了 SPIFFS 的核心操作。你可以在实际项目中直接套用。
3.6 SPIFFS 工作流程
下面这张图展示了 SPIFFS 从挂载到文件读写的完整流程。我画这张图时,特意把关键步骤和错误处理都标出来了。
从这张图可以看出,SPIFFS 的操作流程其实很清晰。挂载是第一步,文件读写是核心,错误处理是关键。我建议你在实际项目中,把挂载和格式化逻辑放在初始化阶段,文件操作封装成独立函数。
总结一下:SPIFFS 是 ESP32 上最常用的文件系统。挂载、格式化、文件读写,这三板斧学会了,后面做网页存储、配置管理就轻松多了。记住:挂载失败就格式化,文件操作完就关闭,路径别搞嵌套。