Base64 Image Transferring between Javascript and Python with Protobuf

We know how to use protobuf with Python

We know how to use protobuf with Javascript

Can we merge it together? I mean to do a transferring with both of the most powerful programming languages: Python and Javascript.

First, let’s define a protocol:

//protoc --python_out=. everyday.proto
//protoc --proto_path=. --js_out=import_style=commonjs,binary:. everyday.protosyntax = "proto3";message OneDay {
    string date = 1;message Content {
        string date = 1;
        string text = 2;
        repeated string image = 3;
    }
    repeated Content content = 2;
}message EveryDay {
    repeated OneDay oneday = 1;
}

Then, create a javascript client with a simple POST:

// author: yingshaoxo@gmail.comconst axios = require('axios').default;
var messages = require('./protocol/everyday_pb');const host = 'http://' + "192.168.31.38" + ':' + "8888"
const upload_url = host + "/api/v1/upload"const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
const Base64 = {
  btoa: (input: string = '') => {
    let str = input;
    let output = '';for (let block = 0, charCode, i = 0, map = chars;
      str.charAt(i | 0) || (map = '=', i % 1);
      output += map.charAt(63 & block >> 8 - i % 1 * 8)) {charCode = str.charCodeAt(i += 3 / 4);if (charCode > 0xFF) {
        throw new Error("'btoa' failed: The string to be encoded contains characters outside of the Latin1 range.");
      }block = block << 8 | charCode;
    }return output;
  },atob: (input: string = '') => {
    let str = input.replace(/=+$/, '');
    let output = '';if (str.length % 4 == 1) {
      throw new Error("'atob' failed: The string to be decoded is not correctly encoded.");
    }
    for (let bc = 0, bs = 0, buffer, i = 0;
      buffer = str.charAt(i++);~buffer && (bs = bc % 4 ? bs * 64 + buffer : buffer,
        bc++ % 4) ? output += String.fromCharCode(255 & bs >> (-2 * bc & 6)) : 0
    ) {
      buffer = chars.indexOf(buffer);
    }return output;
  }
};function arrayBufferToBase64(buffer: Iterable<number>) {
  let binary = '';
  let bytes = new Uint8Array(buffer);
  let len = bytes.byteLength;
  for (let i = 0; i < len; i++) {
    binary += String.fromCharCode(bytes[i]);
  }
  return Base64.btoa(Base64.btoa(binary));
}function make_a_request(text: String, imageBase64List: Array<String>) {
  var oneday = new messages.OneDay()
  let content = oneday.addContent()
  content.setText(text)
  content.setImageList(imageBase64List)
  let data = oneday.serializeBinary()
  data = arrayBufferToBase64(data)
  //console.log(data)
  axios.post(upload_url, {
    action: 'oneday',
    data: data,
  })
  .then(function (response: any) {
    console.log(response);
  })
  .catch(function (error: any) {
    console.log(error);
  });
}

Then, create a Python server to get that message:

# author: yingshaoxo@gmail.com
import base64
import everyday_pb2from PIL import Image
import iodef printit(text):
    print("\n", '-'*10, "\n")
    print(text)def show_image(base64_string):
    #base64_string = base64_string.split(",")[1]
    im = Image.open(io.BytesIO(base64.b64decode(base64_string)))
    #im.save('image.png', 'PNG')
    im.show()everyday = everyday_pb2.EveryDay()app = Sanic(name="freedom")
CORS(app)@app.route('/api/v1/upload', methods=['POST'])
async def user_management(request):
    result = {"status": "wrong"}
    if request.json:
        action = request.json.get("action")
        data = request.json.get("data")
        if action and data:
            printit(data[:30])  # raw data
            data = base64.b64decode(data)  # encoded by our self with our own javascript function
            printit(data[:30])
            data = base64.decodebytes(data)  # encoded by google protocol
            printit(data[:30])  # mergeable bytes data for protocol object
            oneday = everyday_pb2.OneDay()
            oneday.MergeFromString(data)
            printit(oneday.content[0].text)base64_image_string = oneday.content[0].image[0]
            printit(base64_image_string[:30])
            show_image(base64_image_string)everyday.oneday.extend([oneday])
            printit(everyday)result["status"] = "ok"
    return response.json(result)if __name__ == "__main__":
    app.run(host="0.0.0.0", port=8888, debug=True)

How about Python to Javascript?

import json
import everyday_pb2
import base64@app.route('/api/v1/get', methods=['POST'])
def get_today_message():
    everyday = everyday_pb2.EveryDay()
    # add something to everyday
    realdata = everyday.SerializeToString()
    realdata = base64.encodebytes(realdata).decode("ascii")
    data = {
        "realdata": realdata
    }
    return json.dumps(data)
const axios = require('axios').default;
var messages = require('../protocol/everyday_pb');function make_a_request(url: String) {
  var everyday = new messages.EveryDay()
  axios.post(url, {
    action: 'get_everyday',
  })
  .then(function (response: any) {
    console.log(response);
    let data = response.data;
    let realdata = data.realdata
    let everyday = messages.EveryDay.deserializeBinary(realdata)
    console.log(everyday)
    let oneday_list = everyday.getOnedayList()
    oneday_list.map((oneday) => {
      console.log(oneday.getDate())
    })
  })
  .catch(function (error: any) {
    console.log(error);
  });
}