1

In the past, I was experimenting with a method of uploading files to the ESP8266 using the stock ESP8266WebServer class as the backend. The frontend is embedded in the firmware, and it runs offline, so I can't install something like JQuery. Further testing (i.e. deleting literally everything that wasn't the file upload code itself) has revealed that the culprit is the XHR file upload method I was using:

I was following the example here for setting up a file uploader (I want to have a progress bar), but the server never sees the upload object properly update. The server apparently has a status parameter that will have one of the following states as a file is uploaded:

enum HTTPUploadStatus { UPLOAD_FILE_START, UPLOAD_FILE_WRITE, UPLOAD_FILE_END,
                        UPLOAD_FILE_ABORTED };

The way the server handles uploads is that it runs a callback function every time there's an update or a new file chunk to be handled. On the very first chunk, it is supposed to have the state UPLOAD_FILE_START so I can open the file to write in SPIFFS, but for some reason the state never reaches that state. It will instead skip it and run several writes before the connection just mysteriously drops (and sometimes triggers the ABORTED state).

Using a HTML Form object works perfectly, but of course that won't list progress.

In my previous question, the only clue I got was that I need to set the appropriate content-type, which sounds quite plausible to me, but I can't seem to find a definitive list nor do I know what one is used by an uploading form (I can't dissect a Form with a submit button since there's nothing there to begin with). No further details were offered, even when asked, so that's all I really know. I'm not very familiar with the inner workings of HTML and sending data over the Internet, so I'm not sure where to go from here.

Now, I did find a seemingly-working uploader (below) for a different application, but it's specifically designed for a String that is massaged into looking like a file, not an actual file. I don't know how to use this approach if I have an actual file instead of a Blob or a string that I can append to a POST request. My previous attempt did actually have a content-type, but I'm not sure if that was/is the right one to use.

So, my question to you is, how do I properly upload a file using XHR so that it looks like a real Form doing it? And further, for my "fake file" approach, since I'm also using that elsewhere, is there something subtly wrong that will make it not work properly too? After all, this seems to use the exact same approach as the example I was trying to use for the actual file upload, but this one works! (for now...)

  /*
  var xhttp = new XMLHttpRequest();
  xhttp.open("POST", "/jsonupload", true);
  xhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
  xhttp.send("JSONCONF="+dataToSend);
  */
  var formData = new FormData(); //from https://stackoverflow.com/questions/2198470/javascript-uploading-a-file-without-a-file/
  blob = new Blob([dataToSend], {
    type: 'plain/text'
  });
  formData.append('file', blob, fname);
  var request = new XMLHttpRequest();
  request.open('POST', '/jsonupload');
  request.send(formData);

As noted, in the case of trying to upload a string as a file, I have read this question, but it has two answers with three different methods. I'm not sure which one I'm supposed to use just for the fake-file method, and have no idea how or if I can adapt them to a real file. The one after "EDIT" in particular seems to assume much more knowledge about HTTP than I have!

My current file upload form is this (note how lacking in features it is...):

  <html><body><form method='POST' action='' enctype='multipart/form-data'>
  <input type='file' name='update'>
  <input type='submit' value='Update'>
  </form>
  </body></html>

Update to include server's code: The server's code is slightly different based on the application. The one I'd like to get working (the file upload) uses the backend from ESP8266HTTPUpdateServer (with a minor change to get the file upload percentage and print that). When I did the testing and determined that it's something in the XHR, I used the stock backend (without any of my changes) from https://github.com/esp8266/Arduino/tree/5a47cab77d396afeb842c9161327f9f4dca7c833/libraries/ESP8266HTTPUpdateServer/src.

The one for the "fake file" JSON upload is as follows (basically the same, but it writes to SPIFFS as a file, of course):

void JsonUploadStart() {
  HTTPUpload &upload = server.upload();
  if (upload.status == UPLOAD_FILE_START) {
    sendFileOk = false;
    menu.log(F("Config file upload start"));
    int contentLength = server.header(F("Content-Length")).toInt();
    String filename = upload.filename;
    filename = controller.getFilename(filename);
    if (filename.length() > 15) {
      filename.remove(15);
    }
    if (config.config_file != filename) {
      config.config_file = filename;
      config.updateStrandConfig();
      menu.log(F("Changed loaded LED config"));
    }
    menu.log(String(F("Name: ")) + filename);
    menu.log(String(F("Filesize: ")) + contentLength);
    if (filename != DEFAULT_COLOR_CONFIG) {
      fsUploadFile = SPIFFS.open(controller.getRealFile(filename), "w");
      sendFileOk = true;
    }
  } else if (upload.status == UPLOAD_FILE_WRITE) {
    Serial.print('.');
    if (fsUploadFile && sendFileOk)
      fsUploadFile.write(upload.buf,
                         upload.currentSize); // Write the received bytes to the file
  } else if (upload.status == UPLOAD_FILE_END) {
    if (fsUploadFile && sendFileOk) { // If the file was successfully created
      fsUploadFile.close();           // Close the file again
      menu.log(String(F("Size: ")) + upload.totalSize);
    } else {
      server.send(500, F("text/plain"), F("500: couldn't create file"));
    }
  }
}
10
  • you don't use the ESP8266HTTPUpdateServer library? did form upload work with your server implementation?
    – Juraj
    Sep 16 '20 at 8:09
  • Form upload works, as stated. This is actually for a variant of the listed library, but I was trying to add a progress bar to the webpage, which requires Javascript and seemingly can't be done with a regular Form. I'd also like confirmation that the fake-file system I'm using for my configs isn't being done wrong and won't suddenly break on me. Sep 16 '20 at 19:55
  • add server's code to the question? and the form which works please too. do you handle /jsonupload path?
    – Juraj
    Sep 16 '20 at 20:02
  • I was trying this with the ESP8266HTTPUpdateServer backend for testing. Specifically github.com/esp8266/Arduino/tree/… since my code was based on it (even if there have been updates since to that library, the core code is the same). Sep 17 '20 at 22:01
  • 1
    Oops. Adding... Sep 19 '20 at 0:14

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.