Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

.NET MVC deserialize byte array from JSON Uint8Array

I'm using js-scrypt (https://github.com/tonyg/js-scrypt) on my client-side web application to hash and salt passwords before posting them to my server-side .NET MVC application to be hashed and salted again. This JavaScript library implements byte arrays as JavaScript Uint8Arrays. How do I get my MVC Controller to deserialize my JSON Uint8Array to a byte[]?

JavaScript Example: (AJAX.Post is a library I wrote, myUint8Array serializes properly)

AJAX.Post('www.example.com/SendByteArray', { myByteArray: myUint8Array }, Callback);

C# Example: (In my default controller)

[HttpPost]
public async Task<JsonResult> SendByteArray(byte[] myByteArray) {

}

In this example myByteArray is always null. I've tried a couple different approaches based on converting to strings and then back to a byte[] but I haven't been able to get the correct value. It would be greatly preferred if I could somehow implement the code into .NET's JSON deserializer directly so that the code above works exactly as is, because I have a few other projects where I could do some cool things if I could pass byte arrays directly between the server-side and client-side applications.

like image 499
user1084447 Avatar asked Jul 24 '13 21:07

user1084447


4 Answers

For now the only method I could get to work was to base64 encode the Uint8Array, capture it as a string in C#, and then convert that string to a byte[].

JavaScript:

AJAX.Post('www.example.com/SendByteArray', { strByteArray: btoa(String.fromCharCode.apply(null, myUint8Array)) }, Callback);

C#:

[HttpPost]
public async Task<JsonResult> SendByteArray(string strByteArray) {
    byte[] myByteArray = Convert.FromBase64String(strByteArray);
}
like image 77
user1084447 Avatar answered Nov 15 '22 09:11

user1084447


I faced the same issue and after lot of R&D. I came to few conclusions.

Approach 1: C# cannot deserialize javascript types arrays(UInt8Array, UInt16Array etc.). The data should be copied in to normal java script array from typed array and that data should be sent. At the receiving end (C# endpoint method) the parameter should be integer array instead of byte array. If byte array is placed, data received as null at the end point. Received integer array should be converted to byte array for file recovery.

Approach 2: Another option to send typed array data instead of copying javascript typed array data into normal array is to send the typed array data as it is and at the receiving end (C# endpoint method), the method parameter should object. This object should be iterated using some linq and should be converted to byte array for file recovery.

Both are approaches discovered above are very slow according to me. When I am sending 3 files each of 5MB size, my browser (IE 10 browser) memory consumption increased exponentially while sending the data through Ajax request. I am still not able to figure out the issue. If someone is able to send byte array using Ajax please let me know.

Approach 3: Third approach is to convert the byte array to base64 encoded string and send it. Though this increases the file size by 33% this approach is far better than above two. I am able to send 15 MB file easily and memory consumption of browser is aroung 80MB while sending this 3 files and consumption become less once the files are sent.

Important: Please deallocate memory of the variable after reading the file content. Garbage collection in IE is not that good. I faced a lot of issues with memory consumption after reading file using fileReader. Deallocate all the unused variable and byte array content of the file when they are no longer needed.

Please let me know if am wrong.

like image 29
Jagadish Dharanikota Avatar answered Nov 15 '22 07:11

Jagadish Dharanikota


I wasn't able to change server side's, so I needed to keep the byte[] type on server's endpoint. In order to solve this I ended up transforming my Uint8Array to a simple array of bytes, which JSON parses as an array instead of an object.

            const byteArray = [];
            Object.keys(contentUint8Array).forEach((key) =>{
                const byteForKey = contentUint8Array[key];
                byteArray.push(byteForKey);
            });

This way .Net endpoint was able to deserialize to a Byte array.

like image 23
jondediego Avatar answered Nov 15 '22 08:11

jondediego


Change your controller action to accept an int[] instead of byte[], then convert to a byte array. The post value can still be a JSON array.

[HttpPost]
public async Task<JsonResult> SendByteArray(int[] myByteArray) {

     byte[] theBytes = myByteArray.Select(i => (byte)i).ToArray();

     // Or any one of a dozen other ways of converting it

}

In order to post to a byte array, you'd have to base-64 encode the bytes on the client, and pass it in as a string.

There may be an alternative, like a filter or attribute or something like that, but this is the easiest way I know of.

like image 43
Joe Enos Avatar answered Nov 15 '22 08:11

Joe Enos