javascriptpythonarraybuffer

Convert Binary File Data To ArrayBuffer Sent From Python As Byte-Array String


I'm developing a Python App which uses PyQt5 and PyQtWebEngine to create a browser window. Using QWebChannel I'm able to send strings back and forth between the Web Application and the Python program. I want to use this feature to load in external files such as GLB Models.

I need to read the file in Python then send the byte data as a string to the Web Application where I convert it into an ArrayBuffer before its usable. I'm loosely following the dissussion here which does the exact same thing except that the file is read using FileReader in JavaScript instead of reading it with Python.

In my Python Code I'm doing this to read the file:

@pyqtSlot(result=str)
def load_binary_data(self):
    file = open("model.glb", "rb")
    byte_array = str(file.read())
    return byte_array

With QWebChannel I can access the above Python function inside my Javascript code. To convert the string to an ArrayBuffer I'm using TextEncoder:

bridge.load_binary_data((byteArrayString)=>{
    var byteArray = new TextEncoder().encode(byteArrayString)
    var arrayBuffer = byteArray.buffer;
})

With ThreeJS I'm supposed to be able to load a model as ArrayBuffer using loader.parse() like this:

const loader = new GLTFLoader()
loader.parse( arrayBuffer, '', ( gltf ) => {
    // do stuff
})

However when I try to load the Model I get this error:

Unexpected token b in JSON at position 0

I'm guessing there is something wrong with how I send the byte array as a string from Python to my Web Application as there is this b'' wrapping around the entire string which GLTFLoader().parse() does not seem to like very much. I'm cannot figure out how to solve this issue, what would be the correct approach here?

How do I correctly convert the byte-array string sent from Python to an ArrayBuffer in my JavScript?


Solution

  • Thanks to @Obaskly I could figure it out!

    This is the Python Code:

    @pyqtSlot(result=str)
    def load_binary_data(self):
        file = open("model.glb", "rb")
        byte_array = file.read()
        bytes_base64 = (base64.b64encode(byte_array)).decode()
        return bytes_base64
    

    And This the JavaScript Code:

    bridge.load_binary_data((base64_string)=>{
    
        var binaryString = atob(base64_string);
        var bytes = new Uint8Array(binaryString.length);
        for (var i = 0; i < binaryString.length; i++) {
            bytes[i] = binaryString.charCodeAt(i);
        }
        var arrayBuffer = bytes.buffer;
    
        const loader = new GLTFLoader()
        loader.parse( arrayBuffer, '', ( gltf ) => {
            // Do Stuff
        })
    })
    

    This works like a charm, the model is loaded without an issue.