Sturm
Sturm

Reputation: 4125

Blazor to Javascript byte array interop

I am trying to pass an array of byte from Blazor Client to a javascript function:

private async void ShowImage()
{
    SelectedImageBytes = await GetImageData();
    if (SelectedImageBytes.Any())
    {
        ReceivedDataLength = SelectedImageBytes.Length;
        //ReceivedDataLength is 131072, which is correct
        JS.InvokeVoidAsync("JS.setImage", SelectedImageBytes, 256, 256);
        
    }

    StateHasChanged();
}

On Javascript side:

function setImage(data, width, height)
{
    console.log("On Javascript I have received an array of " + data.length);
    //data.length is 174764
    console.log(data);

    //...
}

console.log(data) outputs the following:

enter image description here

Which seems to me a base64 string representation of my binary data. According wikipedia the size is incremented approximately by 33% going from byte array to base64 string representation, and this is true for this case: 131072 * 1.33 ~ 174764

My questions then are:

Upvotes: 3

Views: 3014

Answers (2)

Sturm
Sturm

Reputation: 4125

I gave it another go:

C#

public void CallJsUnMarshalled()
{
    var unmarshalledRuntime = (IJSUnmarshalledRuntime)JS;
    unmarshalledRuntime.InvokeUnmarshalled<byte[], int>("JsFunctions.MyFunctionUnmarshalled", MyBytes);
}

Javascript:

function MyFunctionUnmarshalled(bytes)
{
    const dataPtr = Blazor.platform.getArrayEntryPtr(bytes, 0, 4);
    const length = Blazor.platform.getArrayLength(bytes);
    var shorts = new Int16Array(Module.HEAPU8.buffer, dataPtr, length);
    return 0;
}

InvokeUnmarshalled requires a return it appears, therefore int in the template arguments. Instead of this probably a reference to the object has to be returned to dispose it. I would appreciate if someone can comment on this (will javascript free that memory when the byte array is not used anymore?).

First tests show an improvement of a factor of 50!

Upvotes: 4

Just the benno
Just the benno

Reputation: 2601

When you are using the interop service, the documentation says:

InvokeAsync takes an identifier for the JavaScript function that you wish to invoke along with any number of JSON-serializable arguments.

So what you observe is the serialization of your byte array into a base64 string, which is the out-of-the-box behavior. So, you are right. I haven't spotted a way to influence the serialization behavior of the JSInterop service.

The Blazer framework in .NET 5 offers you a way to skip the serialization overhead: IJSUnmarshalledRuntime.

But, in my experiments, it can't handle a byte array or any arrays at all.

To answer your second question, have a look at this discussion.

Convert base64 string to ArrayBuffer

Upvotes: 2

Related Questions