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.proto
syntax = "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.com
const 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_pb2
from PIL import Image
import io
def 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?

Python side:

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)

Javascript side:

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);
});
}

A man who love English and Coding.