ESP32+ESPAsyncWebServer文件上传目录失效问题求助
ESP32 AsyncWebServer 文件上传目录选择失效问题修复
问题现象
使用ESPAsyncWebServer搭建Web服务器,网页包含文件上传控件和目录选择下拉菜单,但文件始终上传到SD卡根目录,directory变量为空,无法指定目标文件夹。
错误原因
- HTML表单嵌套错误:
getWebpage()中创建了两个嵌套的<form>标签,第二个表单会覆盖第一个,导致下拉菜单selektor的参数无法提交到/upload接口。 - 未检查目录是否存在:如果SD卡中没有
dir1/dir2/dir3目录,直接写入会失败,导致文件无法保存到目标路径。 - 参数获取方式问题:在
multipart/form-data类型的请求中,需要使用request->getParam()而非request->arg()来获取表单参数。
修复步骤
- 修复HTML表单结构,删除多余的
<form>标签,确保所有表单控件都在同一个提交到/upload的表单内。 - 改用正确的参数获取方法,在
handleUpload中使用request->getParam("selektor", true)来获取下拉菜单的参数值。 - 添加目录检查与创建,在打开文件前,检查目标目录是否存在,不存在则创建。
- 优化路径拼接,避免路径出现重复的斜杠,确保路径格式正确。
完整修复代码
#include <WiFi.h> #include <AsyncTCP.h> #include <ESPAsyncWebServer.h> #include <SD.h> #include <SPIFFS.h> #include <Wire.h> #include <Adafruit_GFX.h> #include <Adafruit_SSD1306.h> #define SCREEN_WIDTH 128 #define SCREEN_HEIGHT 64 #define OLED_RESET -1 #define SCREEN_ADDRESS 0x3C Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET); const char* ssid = "myWifi"; const char* password = "myPassword"; AsyncWebServer server(80); void setup() { Serial.begin(115200); if (!SD.begin()) { Serial.println("Failed to mount SD card."); return; } // 提前创建三个目标目录(如果不存在) if (!SD.exists("/dir1")) SD.mkdir("/dir1"); if (!SD.exists("/dir2")) SD.mkdir("/dir2"); if (!SD.exists("/dir3")) SD.mkdir("/dir3"); if (!SPIFFS.begin(true)) { Serial.println("An Error has occurred while mounting SPIFFS"); return; } if (!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) { Serial.println(F("SSD1306 allocation failed")); for (;;); } display.clearDisplay(); display.setTextSize(1); display.setTextColor(SSD1306_WHITE); WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { delay(1000); Serial.println("Connecting to WiFi..."); } Serial.println("Connected to WiFi"); server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) { request->send(200, "text/html", getWebpage()); }); server.on("/upload", HTTP_POST, [](AsyncWebServerRequest *request) { request->send(200, "text/plain", "Upload completed"); }, handleUpload); server.begin(); } void handleUpload(AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final) { static File file; static String path; if (!index) { // 获取下拉菜单参数,第二个参数true表示从multipart表单中获取 AsyncWebParameter* param = request->getParam("selektor", true); String directory = param ? param->value() : ""; // 处理空目录的情况,默认上传到根目录 if (directory.isEmpty()) { path = "/" + filename; } else { path = "/" + directory + "/" + filename; } Serial.println("目标路径: " + path); file = SD.open(path, FILE_WRITE); } if (file) { file.write(data, len); // 直接写入整块数据,无需循环 if (final) { file.close(); display.clearDisplay(); display.setCursor(0,0); display.println("文件已上传:"); display.println(filename); display.display(); Serial.println("文件上传完成: " + filename); } } else if (!index) { Serial.println("无法打开文件: " + path); } } void loop() { } String getWebpage() { String page = "<!DOCTYPE html><html><head><title>ESP32 文件上传</title></head><body>"; page += "<form method='POST' action='/upload' enctype='multipart/form-data'>"; page += "<input type='file' name='upload'><br>"; page += "<select name='selektor'>"; page += "<option value='dir1'>目录1</option>"; page += "<option value='dir2'>目录2</option>"; page += "<option value='dir3'>目录3</option>"; page += "</select><br>"; page += "<input type='submit' value='上传'></form></body></html>"; return page; }
关键修复点说明
- 表单结构:删除了多余的
<form action='/submit' method='post'>标签,确保所有控件都属于提交到/upload的表单。 - 参数获取:使用
request->getParam("selektor", true)从multipart表单中提取参数,true参数表示忽略URL中的查询参数,只从表单数据中获取。 - 目录预创建:在
setup()中提前检查并创建三个目标目录,避免因目录不存在导致文件写入失败。 - 写入优化:直接使用
file.write(data, len)代替循环写入,提升效率。
内容的提问来源于stack exchange,提问作者Bruce Brömseklöten
相关产品推荐
相关产品推荐

