Open frankcohen opened 3 years ago
I found the Javascript XMLHttpRequest correctly sends the data to the servlet, but the servlet handling the Chrome html form as above also does a Base64 decode of the data before saving it to SPIFFS or an SD card.
function uploadslice( dacanvas, disnum, slicenum ) {
doneflag = false;
var base64jpg = dacanvas.toDataURL("image/jpeg", 1.0);
var dataURL = base64jpg.replace(/\s/g, '+').replace(/^data:image\/jpeg;base64,/, '');
var oAjaxReq = new XMLHttpRequest();
oAjaxReq.submittedData = dataURL;
oAjaxReq.onload = function (oEvent)
{
var httprq = new XMLHttpRequest();
httprq.onreadystatechange = function() {
if (httprq.readyState == 4 && httprq.status == 200)
console.log("0");
}
httprq.open("GET", "https://192.168.1.1/showslice?filename=/slice" + slicenum + ".jpg&display=" + disnum, true); // true for asynchronou
httprq.send(null);
console.log( "slicenum = " + slicenum );
if ( slicenum == 0 )
{
uploadslice( canvas1, 4, 1 );
}
if ( slicenum == 1 )
{
uploadslice( canvas2, 3, 2 );
}
if ( slicenum == 2 )
{
uploadslice( canvas3, 2, 3 );
}
if ( slicenum == 3 )
{
uploadslice( canvas4, 1, 4 );
}
if ( slicenum == 4 )
{
uploadslice( canvas5, 0, 5 );
}
}
oAjaxReq.open("post", "upload", true);
oAjaxReq.setRequestHeader("Decode64", "true" );
var bound = Date.now().toString(16);
var sBoundary = "----WebKitFormBoundary" + bound;
oAjaxReq.setRequestHeader("Content-Type", "multipart/form-data; boundary=" + sBoundary );
var mydata = "--" + sBoundary + "\r\n";
mydata += "Content-Disposition: form-data; name=\"name\"; filename=\"" + "slice" + slicenum + ".jpg" + "\"\r\n";
mydata += "Content-Type: image/png";
mydata += "\r\n";
mydata += "\r\n";
mydata += dataURL;
mydata += "\r\n";
mydata += "--" + sBoundary + "--";
mydata += "\r\n";
oAjaxReq.send( mydata );
}
// Set up our event listener to run the startup process
// once loading is complete.
window.addEventListener('load', startup, false);
})();
This looks like a bug in ESP32_HTTPS_Server to me.
i came up with a workaround... my Javascript code adds a special HTTP header value and the servlet does an extra step to do the Base64 decoding on the fly:
void handle_upload(HTTPRequest * req, HTTPResponse * res)
{
Serial.println("Handle_upload");
HTTPBodyParser *parser;
parser = new HTTPMultipartBodyParser(req);
bool didwrite = false;
bool decode64 = false;
std::string decval = req->getHeader( "Decode64" );
String decstr = decval.c_str();
if ( decstr == "true" )
{
decode64 = true;
Serial.println( "Decode64 = true" );
}
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);
if ( decode64 && ( readLength>0 ) )
{
size_t outlen;
unsigned char output[ 512 ];
mbedtls_base64_decode(output, 512, &outlen, buf, readLength);
fsUploadFile.write(output, outlen);
fileLength += outlen;
}
else
{
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;
Serial.print( "LITTLEFS totalBytes=" );
Serial.print( LITTLEFS.totalBytes() );
Serial.print( ", usedBytes=" );
Serial.print( LITTLEFS.usedBytes() );
Serial.print( ", free bytes=" );
Serial.println( LITTLEFS.totalBytes() - LITTLEFS.usedBytes() );
if(didwrite)
{ // If the file was successfully created
res->setHeader("Location", "/success");
res->setStatusCode(303);
}
else
{
res->setStatusCode(500);
res->setStatusText("Upload failed");
}
}
This is the decoding code. It uses a Base64 decoder built into the ESP32 operating environment:
if ( decode64 && ( readLength>0 ) )
{
size_t outlen;
unsigned char output[ 512 ];
mbedtls_base64_decode(output, 512, &outlen, buf, readLength);
fsUploadFile.write(output, outlen);
fileLength += outlen;
}
else
{
fsUploadFile.write(buf, readLength);
fileLength += readLength;
}
-Frank
Picojpeg is great. I found it embeded in Bodmer's JPEGDecoder https://github.com/Bodmer/JPEGDecoder/blob/master/src/picojpeg.h and used to display images on a 135x240 pixel OLED display.
I created a 10x10 pixel JPEG encoded image from a Javascript context using this code:
I am using FHessel's HTTPS Server on ESP32 to receive the form post from a Chrome browser.
https://github.com/fhessel/esp32_https_server
Here is my form post handler code for the server side:
The upload works, stores the file in the ESP32 SPIFFS file system (using LittleFS) and here are the file contents (printed out as ASCII character and Hex value):
Picojpeg rejects the file and throws error 19.
I tested Picojpeg using a jpeg file I created in Pixelmator for Mac and Picojpeg works fine. I use the same HTTPS Server code to upload the file contents. I use this HTML:
This uploads the Jpeg file correctly. And Picojpeg decodes the jpeg image correctly and with no error.
I'm not sure where to go next? Any help would be appreciated.
-Frank