You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ESP32+ESPAsyncWebServer文件上传目录失效问题求助

ESP32 AsyncWebServer 文件上传目录选择失效问题修复

问题现象

使用ESPAsyncWebServer搭建Web服务器,网页包含文件上传控件和目录选择下拉菜单,但文件始终上传到SD卡根目录,directory变量为空,无法指定目标文件夹。

错误原因

  1. HTML表单嵌套错误:getWebpage()中创建了两个嵌套的<form>标签,第二个表单会覆盖第一个,导致下拉菜单selektor的参数无法提交到/upload接口。
  2. 未检查目录是否存在:如果SD卡中没有dir1/dir2/dir3目录,直接写入会失败,导致文件无法保存到目标路径。
  3. 参数获取方式问题:在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.25 21:53:14