fhessel / esp32_https_server

Alternative ESP32 Webserver implementation for the ESP32 Arduino Core, supporting HTTPS and HTTP.
MIT License
330 stars 118 forks source link

Javascript example to upload binary jpg file to ESP32 #129

Open frankcohen opened 3 years ago

frankcohen commented 3 years ago

I'll bet this is off-topic... does someone have example Javascript code to do a binary jpg file upload to esp32_https_server. Something like this:

    function uploadimages() {
        canvas.getContext('2d').drawImage( photo1, 0, 0 );
        var dataURL = canvas.toDataURL('image/jpg');

        var oAjaxReq = new XMLHttpRequest();
    oAjaxReq.submittedData = dataURL;

      oAjaxReq.onreadystatechange = function() {
             console.log("Uploading image");
             console.log(oAjaxReq.responseText);
      }
        oAjaxReq.onload = function (oEvent)
        {
             console.log("Uploaded");
        }

        oAjaxReq.open("post", "myfile.jpg", true);

        var sBoundary = "---------------------------" + Date.now().toString(16);
        oAjaxReq.setRequestHeader("Content-Type", "multipart\/form-data; boundary=" + sBoundary);
        var mydata = "--" + sBoundary + "\r\n" + dataURL + "--" + sBoundary + "\r\n" + "--" + sBoundary + "--\r\n";
        oAjaxReq.sendAsBinary( mydata );
    }

I'm looking for an easy way to take a canvas, and upload it through esp32_https_server to the esp32 spiffs file system.

-Frank

frankcohen commented 3 years ago

I read the W3C definition of the multipart/form-data spec at https://www.w3.org/TR/html401/interact/forms.html#h-17.13.4 and wrote this Javascript:

    function uploadimages() {
        canvas.getContext('2d').drawImage( photo1, 0, 0 );
        var dataURL = canvas.toDataURL('image/jpg');

        var oAjaxReq = new XMLHttpRequest();
    oAjaxReq.submittedData = dataURL;

      oAjaxReq.onreadystatechange = function() {
             console.log("Uploading image");
             console.log(oAjaxReq.responseText);
      }
        oAjaxReq.onload = function (oEvent)
        {
             console.log("Uploaded");
        }

        oAjaxReq.open("post", "upload", true);

        var bound = Date.now().toString(16);
        var sBoundary = "---------------------------" + bound + "\r\n";

        oAjaxReq.setRequestHeader("Content-Type", "multipart/form-data; boundary=" + bound );

        //var mydata = "Content-Type: multipart/form-data; boundary=" + bound + "\r\n";
        //mydata += "\r\n"
        var mydata = sBoundary;

        mydata += "Content-Disposition: form-data; name=\"files\"; filename=\"" + "slice1.jpg" + "\"\r\n";
        mydata += "Content-Type: image/jpg\r\n";
    mydata += "Content-Transfer-Encoding: binary\r\n";
        mydata += "\r\n"

        mydata += "abcdefghijklmnopqrstuvwxyz\r\n"

        mydata += sBoundary;
        mydata += "\r\n"
        mydata += "\r\n"

        console.log( "data sent is:");
        console.log( mydata );
        console.log( "frankolo");

        oAjaxReq.send( mydata );
    }

In the ESP32 I am using this to handle the upload:

void handle_upload(HTTPRequest * req, HTTPResponse * res)
{ 
  Serial.println("Handle_upload");

  HTTPBodyParser *parser;
  parser = new HTTPMultipartBodyParser(req);
  bool didwrite = false;

  while(parser->nextField()) {

    std::string name = parser->getFieldName();
    std::string filename = parser->getFieldFilename();
    std::string mimeType = parser->getFieldMimeType();
    Serial.printf("handleFormUpload: field name='%s', filename='%s', mimetype='%s'\n", name.c_str(), filename.c_str(), mimeType.c_str() );

    if ( ! (filename.rfind("/", 0) == 0) )
    {
      filename = "/" + filename;
    }

    Serial.print("handle_upload Name: "); 
    Serial.println(filename.c_str() );

    fsUploadFile = LITTLEFS.open( filename.c_str(), "w");            // Open the file for writing in SPIFFS (create if it doesn't exist)

    size_t fileLength = 0;
    didwrite = true;

    while (!parser->endOfField()) {
      byte buf[512];
      size_t readLength = parser->read(buf, 512);
      fsUploadFile.write(buf, readLength);
      fileLength += readLength;
    }

    fsUploadFile.close();
    res->printf("<p>Saved %d bytes to %s</p>", (int)fileLength, filename.c_str() );
  }

  if (!didwrite) {
    res->println("<p>Did not write any file contents</p>");
  }

  delete parser;

Chrome's console shows:

---------------------------179c3afe26a
Content-Disposition: form-data; name="files"; filename="slice1.jpg"
Content-Type: image/jpg
Content-Transfer-Encoding: binary

abcdefghijklmnopqrstuvwxyz
---------------------------179c3afe26a

and the POST header includes:

Content-Type: multipart/form-data; boundary=179c3afe26a

and esp32_https_server reports:

[HTTPS:I] New connection. Socket FID=63
[HTTPS:I] Request: POST /upload (FID=63)
Handle_upload
[HTTPS:E] Multipart missing last boundary

The ESP32 code works fine with Chrome using the normal:

  res->println("<form action=\"upload\" method=\"post\" enctype=\"multipart/form-data\">"  );
  res->println("<input type=\"file\" name=\"name\">"  );
  res->println("<input class=\"button\" type=\"submit\" value=\"Upload\">"  );
  res->println("</form>"  );

Where should I look next? -Frank