htmlwebserveresp32arduino-esp32

ESP32 with HTML request and ESPAsyncWebServer library


I want to run a simple web server on an ESP32 with the ESPAsyncWebServer library. On a web page you should be able to select a file to upload to an SD card. You should also be able to select from a dropdown menu which of three directories to upload the file to. The file is uploaded, but only to the root directory and not to the corresponding folder. The variable "directory" is always empty. Apparently the request does not work properly. What am I doing wrong? Thanks for your help!

#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 (!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);
  }, 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) {
    String directory = request->arg("selektor");
    path = "/" + directory + "/" + filename;
    file = SD.open(path, FILE_WRITE);
    Serial.println("Verzeichnis: " + path);
  }

  if (file) {
    for (size_t i = 0; i < len; i++) {
      file.write(data[i]);
    }

    if (final) {
      file.close();
      display.clearDisplay();
      display.setCursor(0,0);
      display.println("Datei hochgeladen:");
      display.println(filename);
      display.display();
    }
  }
}

void loop() {
}

String getWebpage() {
    String page = "<!DOCTYPE html><html><head><title>ESP32 Option Selector</title></head><body>";
    page += "<form method='POST' action='/upload' enctype='multipart/form-data'><input type='file'      name='upload'><br><select name='selektor'>";
    page += "<form action='/submit' method='post'>";
    page += "<option value='dir1'>Directory 1</option>";
    page += "<option value='dir2'>Directory 2</option>";
    page += "<option value='dir3'>Directory 3</option>";
    page += "</select><input type='submit' value='Upload'></form></body></html>";
  return page;
}

Solution

  • The API for ESP32AsyncWebServer POST request has 3 callback functions as

    server.on("/uri", HTTP_POST, onRequest, onFileUpload, onBody);
    

    Each callback has different parameters for different application scenario.

    Which callback function to use depends on the type of POST request and the mix of the data sent from the client to the server. the onBody() is used if it only contains a post request payload (e.g. a serialised json object), the onFileUpload() callback is used when you request only contains a file to be uploaded where the filename and file data, length has been provided as the callback function parameters. For multipart payload, it could contains both file(s) and further request body, you would need to use the onRequest() callback, the onRequest() function parameter only contains the request object, but you could get all kind the data you need by calling relevant request methods.

    So for your case, you will need to use the onRequest() callback.

    
    void onRequest(AsyncWebServerRequest *request) {
      
      if (request->hasArg("upload") && request->hasArg("selektor")) {
        String path = "/" + request->arg("selektor") + "/" + request->arg("upload");
        Serial.printf("path %s\n", path.c_str());
        request->send(200, "text/plain", "Ok");
      }
      else {
        request->send(400, "text/plain", "Bad request");
      }
    
    }
    
    void setup() {
      
      // other setup codes omitted here
    
      server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) {
        request->send(200, "text/html", getWebpage());
      });
    
      server.on("/upload", HTTP_POST, onRequest);
    
      server.begin();
    }