3

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:

  • How to pass and receive a byte array from Blazor (C#) to Javascript without converting it to a string
  • If the previous is not possible, what is the best way to convert the base64 string to byte array on Javascript side.
Sturm
  • 3,968
  • 10
  • 48
  • 78

2 Answers2

4

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!

Sturm
  • 3,968
  • 10
  • 48
  • 78
  • This worked great, thanks so much! For anyone else that needs to return a string from the javascript function i.e. you have to return a binding to the string like so: `return BINDING.js_string_to_mono_string(url);` This is noted [here](https://learn.microsoft.com/en-us/aspnet/core/blazor/javascript-interoperability/call-javascript-from-dotnet?view=aspnetcore-5.0#unmarshalled-javascript-interop-1) and the function name is subject to change in the future. – mlienau Apr 01 '22 at 02:06
2

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

Just the benno
  • 2,306
  • 8
  • 12
  • Looks very promising. Do you have an idea why it is not working? – Sturm Nov 12 '20 at 14:34
  • I guess that an array is not a native javascript type, unlike numbers or strings. It works fine with an object composed of numbers, strings, and nested objects composed of numbers and strings. :) Instead of the array, it seems that a kind of identifier (a number that reminds me a bit of C pointer) is injected into the javascript method. I can imagine that this number with some calls to other javascript methods could lead you near the array. – Just the benno Nov 12 '20 at 17:29
  • I followed your hint of IJSUnmarshalledRuntime and somehow managed it to work, thanks! – Sturm Mar 09 '21 at 09:20