esp32_https_server icon indicating copy to clipboard operation
esp32_https_server copied to clipboard

Javascript example to upload binary jpg file to ESP32

Open frankcohen opened this issue 4 years ago • 1 comments

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 avatar May 31 '21 06:05 frankcohen

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

frankcohen avatar May 31 '21 18:05 frankcohen