Chat freely about anything...

User avatar
By treii28
#53541 I'm trying to figure out how the upload functionality works in the ESP8266WebServer, but - sorry to say it - the documentation SUCKS.
I see there are two callbacks and the second one seems to be evoked more than once I'm assuming since it tests for three different states to decide whether to open a file/output handle, stream content to the handle, or close the handle. The other seems to be what is called when it's all finished.

Most of the examples I see just upload to the root of the SD card and don't check any other parameters. I wrote a function to check for a 'dir' parameter and then check to see if the 'dir' exists and is in fact a directory. I then managed to get the upload.filename concatenated to it. But When I try to put it somewhere in the handleUpload, I get crash after crash no matter how I try to place it.

So how in the hell do you parse a parameter in conjunction with the handleUpload functionality and still report errors and messages properly? i.e. if the directory doesn't exist, I want it to fail out due to that without even doing the upload. Otherwise, if it complete successfully, I want it to send a 200 with a little more user friendly message than a blank screen with a 200 code!

I tried something like this using the SD web server example. I added two String variables 'serverError' and 'serverMessage' globally and clear them out before starting each 'loop' call to handleClient. (I also clear them after running my returnFail or returnMessage calls)

In setup:
Code: Select all  server.on("/edit", HTTP_POST, []() {
    if (serverError != "") {
      returnFail(serverError);
    } else if (serverMessage != "") {
      returnMsg(serverMessage);
    } else {
      returnMsg("upload");
    }
    serverMessage = "";
    serverError = "";
  }, handleFileUpload);


handleFileUpload:
Code: Select allvoid handleFileUpload() {
  if (server.uri() != "/edit") return;
  HTTPUpload& upload = server.upload();

  if (upload.status == UPLOAD_FILE_START) {
    String fn = getDirArg() + upload.filename;
    if (serverError == "") {
      serverMessage = "uploading";
      DBG_OUTPUT_PORT.print("Upload: START, filename: ");
      DBG_OUTPUT_PORT.println(fn);
      if (SD.exists(fn)) {
        serverMessage += " over";
        DBG_OUTPUT_PORT.println("file exists, deleting!");
        // todo check to make sure file isn't a directory
        SD.remove(fn);
      }
      serverMessage += " " + fn;
      uploadFile = SD.open(fn, FILE_WRITE);
      DBG_OUTPUT_PORT.println("opened!");
    }
  } else if ((upload.status == UPLOAD_FILE_WRITE) && (serverError == "")) {
    if (uploadFile) uploadFile.write(upload.buf, upload.currentSize);
    DBG_OUTPUT_PORT.print("Upload: WRITE, Bytes: ");
    DBG_OUTPUT_PORT.println(upload.currentSize);
  } else if ((upload.status == UPLOAD_FILE_END) && (serverError == "")) {
    if (uploadFile) uploadFile.close();
    DBG_OUTPUT_PORT.print("Upload: END, Size: ");
    DBG_OUTPUT_PORT.println(upload.totalSize);
  }
}


getDirArg:
Code: Select allString getDirArg() {
  String d = (server.hasArg("dir")) ? server.arg("dir") : "";
  d.trim();

  char dChar[d.length() + 1];
  d.toCharArray(dChar, d.length() + 1);

  if ((d != "/") && (d != ""))  {
    if (SD.exists(dChar)) {

      File df = SD.open(d);
      if (df.isDirectory()) {
        if (!(d.endsWith("/"))) d += "/"; // add trailing slash if needed
      } else {
        serverError = "GETPATH: PATH NOT DIR";
      }
      df.close();
    } else {
      serverError = "GETPATH: PATH NOT EXIST";
    }
  }
  return d;
}


return with status functions:
Code: Select allvoid returnOK() {
  server.send(200, "text/plain", "");
}
void returnMsg(String msg) {
  server.send(200, "text/plain", msg + " successful\r\n");
}
void returnFail(String msg) {
  server.send(500, "text/plain", msg + "\r\n");
}
User avatar
By treii28
#53542 OK, I think I found my error.
In the getDirArg() function, I was getting crashes on sd.exists() when using the string value for the filename. There is apparently a known bug that claims to be fixed on this, as it is supposed to handle the string, but it was still crashing for me and I was able to get around it by converting the string to a character array. I forgot the example code also had another SD.exists() call on the file to see if it existed already.
Allegedly the bug also effects the SD.remove() function so I converted the full path to a char and then used the char for both functions. re-compiling as I type. Testing shortly....

Yup, that did it. I guess I can make a wrapper for SD.exists and SD.remove to handle a string value until the bug is fixed.

UPLOAD_FILE_START portion of handleFileUpload:
Code: Select all    String fn = getDirArg() + upload.filename;
    if (serverError == "") {
      serverMessage = "uploading";
      DBG_OUTPUT_PORT.print("Upload: START, filename: ");
      DBG_OUTPUT_PORT.println(fn);
      char fnChar[fn.length()+1];
      fn.toCharArray(fnChar,fn.length()+1);
      if (SD.exists(fnChar)) {
        serverMessage += " over";
        DBG_OUTPUT_PORT.println("file exists, deleting!");
        // todo check to make sure file isn't a directory
        SD.remove(fnChar);
      }
      serverMessage += " " + fn;
      uploadFile = SD.open(fn, FILE_WRITE);
      DBG_OUTPUT_PORT.println("opened!");
User avatar
By treii28
#53543 I was struggling finding the quick method and found it looking over other code. To use the string more directly (cleaner) they can be called as:

Code: Select allSD.exists((char *) pathString.c_str());
SD.remove((char *) pathString.c_str());


Ugh, struggling learning C++ (I haven't used it since college about 20 years ago)