Post Raspberry Pi image to Google Drive via HTTP Post not working

Hi, I am trying to post a Raspberry Pi camera image to Goggle Drive along with some related information to Google Sheets using HTTP MultiPart Request (post) and a Google Script.

The data is successfully recorded in the Google Sheet but the image, although successfully transferred is not what it should be and is not readable as image.

My Project is as below:
1- NodeRed nodes
[{"id":"3630c78a.035728","type":"camerapi-takephoto","z":"5dd68f45.ecadb","filemode":"1","filename":"car-photo.jpeg","filedefpath":"0","filepath":"/home/pi/Pictures/","fileformat":"jpeg","resolution":"3","rotation":"0","fliph":"0","flipv":"0","brightness":"50","contrast":"0","sharpness":"0","quality":"","imageeffect":"none","agcwait":"","name":"Take Photo","x":230,"y":220,"wires":[["57df1ca5.e60f24"]]}]

2- Function node content:

var image = msg.payload;
msg.headers = {};
msg.headers["content-type"] ='multipart/form-data';
msg.payload = {};
msg.payload['myFile'] = image;
msg.payload['myToken'] = 'zrxC';
msg.payload['myFilename'] = 'myCarPic.jpg';
msg.payload['myFoldername'] = 'ESP32-CAM';
msg.payload['Station'] = 'Hawaii';
msg.payload['lat'] = '1,23';
msg.payload['long'] = '4.32';
msg.payload['maxSpeed'] = '40';
msg.payload['radarSpeed'] = '78';
return msg;

2- Google Script

var timeZone = "GMT";
var dateTimeFormat   = "dd/MM/yyyy HH:mm:ss";
var logSpreadSheetId = "1W1ywsEkfKNFSqsSdAgQbjFgzHO8LDaxv6mNzTP9h4t8";
var dateTime         = Utilities.formatDate(new Date(), timeZone, dateTimeFormat);

function doPost(e) {
  var myFoldername = e.parameter.myFoldername;
  var myFile       = e.parameter.myFile;
  var myFilename   = Utilities.formatDate(new Date(), timeZone, "ddMMyyyyHHmmss")+".jpg";
  var myToken      = e.parameter.myToken;
  
  var Station    = e.parameter.Station;
  var lat        = e.parameter.lat;
  var long       = e.parameter.long;
  var maxSpeed   = e.parameter.maxSpeed;
  var radarSpeed = e.parameter.radarSpeed;  
  
  var contentType = myFile.substring(myFile.indexOf(":")+1, myFile.indexOf(";"));
  var data = myFile.substring(myFile.indexOf(",")+1);
  data = Utilities.base64Decode(data);
  var blob = Utilities.newBlob(data, contentType, myFilename);
  
  // Save a captured image to Google Drive.
  var folder, folders = DriveApp.getFoldersByName(myFoldername);
  if (folders.hasNext()) {
    folder = folders.next();
  } else {
    folder = DriveApp.createFolder(myFoldername);
  }
  var file = folder.createFile(blob);    
  file.setDescription("Uploaded by " + myFilename);
  
  var imageID  = file.getUrl().substring(file.getUrl().indexOf("/d/")+3,file.getUrl().indexOf("view")-1);
  var imageUrl = "https://drive.google.com/uc?id="+imageID;

  addLog(myFilename,imageUrl,Station,lat,long,maxSpeed,radarSpeed);
 
  return  ContentService.createTextOutput(myFoldername+"/"+myFilename+"\n"+imageUrl+"\n");  //+res);
}

function addLog(myFilename,imageUrl,Station,lat,long,maxSpeed,radarSpeed) {
  var spr   = SpreadsheetApp.openById(logSpreadSheetId);
  var sheet = spr.getSheets()[0];
  var data  = sheet.getDataRange().getValues();
  var pos   = sheet.getLastRow();
  
  var rowData  = [];
  
  if(!pos>0){
    pos = 1;
    rowData[0]   = "Data";
    rowData[1]   = "Image";
    rowData[2]   = "URL";
    rowData[3]   = "Station";
    rowData[4]   = "Lat";
    rowData[5]   = "Long";
    rowData[6]   = "MaxSpeed";
    rowData[7]   = "RadarSpeed";  
    rowData[8]   = "NumPlate";
    rowData[9]   = "Manufacturer";
    rowData[10]  = "Model";
    rowData[11]  = "Color";
    var newRange = sheet.getRange(pos, 1, 1, rowData.length);
    newRange.setValues([rowData]);
  } 
  pos     = pos +1;
  rowData = [];
  rowData[0]   = dateTime;
  rowData[1]   = myFilename;
  rowData[2]   = imageUrl;
  rowData[3]   = Station;
  rowData[4]   = lat;
  rowData[5]   = long;
  rowData[6]   = maxSpeed;
  rowData[7]   = radarSpeed;  
  var newRange = sheet.getRange(pos, 1, 1, rowData.length);
  newRange.setValues([rowData]);
}

3- Google Drive image file:

Using this process the "image" file is always 32bytes.
In the Base64 node I tried "convert buffer to base64" and "Encode as base64", result is the same.

NOTE: The Google Script works fine when I transfer image and data using an ESP32-CAM (encoding the image as base64).

ESP32-CAM c++ Sketch:

/*
ESP32-CAM (Save a captured photo to SD card, Google Drive, LineNotify)
Author : ChungYi Fu (Kaohsiung, Taiwan)  2019-9-16 22:30
https://www.facebook.com/francefu

Google Apps Script
https://github.com/fustyles/webduino/blob/gs/SendCapturedImageToGoogleDriveAndLinenotify_doPost.gs
You must allow anyone and anonymous to execute the google script.
How to add a new script
https://www.youtube.com/watch?v=f46VBqWwUuI

https://script.google.com/home
https://script.google.com/home/executions
https://drive.google.com/drive/my-drive
*/

// Enter your WiFi ssid and password
const char* ssid     = "SSID";   //your network SSID
const char* password = "pass";   //your network password

const char* myDomain = "script.google.com";
String myScript = "/macros/s/AKxJLoDA1fycbrJubn4f-YoMGQxqX5oVSZWuPuegxmQ77__KEJzo1O0/exec";    //Google Apps Script ID.
String myLineNotifyToken = "myToken=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";    //Line Notify Token
String myFoldername = "&myFoldername=ESP32-CAM";
String myFilename = "";
String myImage = "&myFile=";

#include <WiFi.h>
#include <WiFiClientSecure.h>
#include <HTTPClient.h>
#include "soc/soc.h"
#include "soc/rtc_cntl_reg.h"
#include "Base64.h"
#include "esp_camera.h"
#include <EEPROM.h>

// WARNING!!! Make sure that you have either selected ESP32 Wrover Module,
//            or another board which has PSRAM enabled

//CAMERA_MODEL_AI_THINKER
#define PWDN_GPIO_NUM     32
#define RESET_GPIO_NUM    -1
#define XCLK_GPIO_NUM      0
#define SIOD_GPIO_NUM     26
#define SIOC_GPIO_NUM     27

#define Y9_GPIO_NUM       35
#define Y8_GPIO_NUM       34
#define Y7_GPIO_NUM       39
#define Y6_GPIO_NUM       36
#define Y5_GPIO_NUM       21
#define Y4_GPIO_NUM       19
#define Y3_GPIO_NUM       18
#define Y2_GPIO_NUM        5
#define VSYNC_GPIO_NUM    25
#define HREF_GPIO_NUM     23
#define PCLK_GPIO_NUM     22

void setup(){
  WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0);
  
  Serial.begin(115200);
  delay(10);
  
  WiFi.mode(WIFI_STA);

  Serial.println("");
  Serial.print("Connecting to ");
  Serial.println(ssid);
  WiFi.begin(ssid, password);  
  
  long int StartTime=millis();
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    if ((StartTime+10000) < millis()) break;
  } 

  Serial.println("");
  Serial.println("STAIP address: ");
  Serial.println(WiFi.localIP());
    
  Serial.println("");

  if (WiFi.status() != WL_CONNECTED) {
    Serial.println("Reset");
    delay(1000);
    ESP.restart();
  }

  camera_config_t config;
  config.ledc_channel = LEDC_CHANNEL_0;
  config.ledc_timer = LEDC_TIMER_0;
  config.pin_d0 = Y2_GPIO_NUM;
  config.pin_d1 = Y3_GPIO_NUM;
  config.pin_d2 = Y4_GPIO_NUM;
  config.pin_d3 = Y5_GPIO_NUM;
  config.pin_d4 = Y6_GPIO_NUM;
  config.pin_d5 = Y7_GPIO_NUM;
  config.pin_d6 = Y8_GPIO_NUM;
  config.pin_d7 = Y9_GPIO_NUM;
  config.pin_xclk = XCLK_GPIO_NUM;
  config.pin_pclk = PCLK_GPIO_NUM;
  config.pin_vsync = VSYNC_GPIO_NUM;
  config.pin_href = HREF_GPIO_NUM;
  config.pin_sscb_sda = SIOD_GPIO_NUM;
  config.pin_sscb_scl = SIOC_GPIO_NUM;
  config.pin_pwdn = PWDN_GPIO_NUM;
  config.pin_reset = RESET_GPIO_NUM;
  config.xclk_freq_hz = 20000000;
  config.pixel_format = PIXFORMAT_JPEG;
  config.frame_size = FRAMESIZE_VGA;  // UXGA|SXGA|XGA|SVGA|VGA|CIF|QVGA|HQVGA|QQVGA
  config.jpeg_quality = 10;
  config.fb_count = 1;
  
  esp_err_t err = esp_camera_init(&config);
  if (err != ESP_OK) {
    Serial.printf("Camera init failed with error 0x%x", err);
    delay(1000);
    ESP.restart();
  }
}

void loop(){
  EEPROM.begin(sizeof(int)*4);
  EEPROM.write(0, EEPROM.read(0)+1);
  EEPROM.commit(); 

  myFilename = "&myFilename="+String(EEPROM.read(0))+".jpg";
  saveCapturedImage2GoogleDrive();
  
  delay(72000);
}

void saveCapturedImage2GoogleDrive() {
  Serial.println("Connect to " + String(myDomain));
  WiFiClientSecure client;
  
  if (client.connect(myDomain, 443)) {
    Serial.println("Connection successful");
    
    camera_fb_t * fb = NULL;
    fb = esp_camera_fb_get();  
    if(!fb) {
      Serial.println("Camera capture failed");
      delay(1000);
      ESP.restart();
      return;
    }
  
    char *input = (char *)fb->buf;
    char output[base64_enc_len(3)];
    String imageFile = "data:image/jpeg;base64,";
    for (int i=0;i<fb->len;i++) {
      base64_encode(output, (input++), 3);
      if (i%3==0) imageFile += urlencode(String(output));
    }
    String Data = myLineNotifyToken+myFoldername+myFilename+"&Station=RioFundo"+"&lat=12.30"+"&long=23.1"+"&maxSpeed=60"+"&radarSpeed=80"+myImage;
    Serial.print("Data to Post: ");
    Serial.println(Data);
    
    esp_camera_fb_return(fb);
    
    Serial.println("Send a captured image to Google Drive.");
    
    client.println("POST " + myScript + " HTTP/1.1");
    client.println("Host: " + String(myDomain));
    client.println("Content-Length: " + String(Data.length()+imageFile.length()));
    client.println("Content-Type: application/x-www-form-urlencoded");
    client.println();
    
    client.print(Data);
    int Index;
    for (Index = 0; Index < imageFile.length(); Index = Index+1000) {
      client.print(imageFile.substring(Index, Index+1000));
    }
    
    Serial.println("Waiting for response from Google Drive");
    long int StartTime=millis();
    while (!client.available()) {
      Serial.print(".");
      delay(100);
      if ((StartTime+10000) < millis()) {
        Serial.println();
        Serial.println("No response.");
        break;
      }
    }
    Serial.println();   
    while (client.available()) {
      Serial.print(char(client.read()));
    }

    client.stop();
  
    myDomain = "api.openalpr.com";
    myScript = "/v2/recognize";
    Data  = "secret_key=mySecretOpenALPRKey&recognize_vehicle=1&country=br&return_image=0&topn=10";
    if (client.connect(myDomain, 443)){
      Serial.println("Connection to api.openalpr.com - OK");

      client.println("POST " + myScript );  //+ " HTTP/1.1");
      client.println("Host: " + String(myDomain));
      client.println("Content-Length: " + String(Data.length()+imageFile.length()));
      client.println("Content-Type: application/x-www-form-urlencoded");
      client.println();
      client.print(Data);
     
      Serial.println("Posting Image...");
      for (int i = 0; i < imageFile.length(); i=i+1000) {
        client.print(imageFile.substring(i, i+1000));
      }
      Serial.println("Image sent to openalpr.com...");
    }else{
      Serial.println("Connection to api.openalpr.com - Fail");
    }

    Serial.println("Waiting for response from api.openalpr.com");
    StartTime=millis();
    while (!client.available()) {
      Serial.print(".");
      delay(100);
      if ((StartTime+10000) < millis()) {
        Serial.println();
        Serial.println("No response.");
        break;
      }
    }
    Serial.println();   
    while (client.available()) {
      Serial.print(char(client.read()));
    }
     
    client.stop(); 
     
  }else {   
    Serial.println("Connected to " + String(myDomain) + " failed.");
  }
  
}


//https://github.com/zenmanenergy/ESP8266-Arduino-Examples/
String urlencode(String str){
    String encodedString="";
    char c;
    char code0;
    char code1;
    char code2;
    for (int i =0; i < str.length(); i++){
      c=str.charAt(i);
      if (c == ' '){
        encodedString+= '+';
      } else if (isalnum(c)){
        encodedString+=c;
      } else{
        code1=(c & 0xf)+'0';
        if ((c & 0xf) >9){
            code1=(c & 0xf) - 10 + 'A';
        }
        c=(c>>4)&0xf;
        code0=c+'0';
        if (c > 9){
            code0=c - 10 + 'A';
        }
        code2='\0';
        encodedString+='%';
        encodedString+=code0;
        encodedString+=code1;
        //encodedString+=code2;
      }
      yield();
    }
    return encodedString;
}

*------------------------------------------------------------------
Assistance welcome.
Thanks

This topic was automatically closed 60 days after the last reply. New replies are no longer allowed.