简体   繁体   中英

.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.

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

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.

I used the following code for uploading large files in chunks from Angular 5 to ASP Core Web API. On the client side - converting ArrayBuffer to Base64String:

private arrayBufferToBase64String(buffer: ArrayBuffer) {
  let binaryString = ''
  var bytes = new Uint8Array(buffer);
  for (var i = 0; i < bytes.byteLength; i++) {
    binaryString += String.fromCharCode(bytes[i]);
  }

  return window.btoa(binaryString);
}

Posting the string (this is the Angular 2+ version):

var data = {
  data: this.arrayBufferToBase64String(arrayBuffer)
}

this.httpClient.post(url, data)

On the server (C#):

var bytes = Convert.FromBase64String(part.Data);

part is a model class:

public class FilePartModel
{
    public string Data { get; set; }
}

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.

you must Use Jason.stringify To Serialize it And set Some Attribute of ajax .This is Example That I Use And It Works

            var list = [];

           //Fill list array

            var parameters = {};
            parameters = JSON.stringify({ "Your parameter": list });
            jQuery.ajax({
                url: '@Url.Action("Your Action")';,
                type: 'POST',
                contentType: 'application/json; charset=utf-8',
                dataType: "html",
                traditional: true,
                data: parameters,
                success: function (data, textStatus, jqXHR) {
                   //TODO: on success
                },
                error: function (data) {
                       //TODO: on fail
                }    
            });

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

    }

     OR Do this Way

    [HttpPost]
    public async Task<JsonResult> SendByteArray(int[] myByteArray) {
      convert to byte here
    }

Building on the solution of user1084447 (serializing and deserializing to a base64 string), this works pretty good for serialization:

var serialized = fromByteArray(myUint8Array);

Where the fromeByteArray function comes from this github project . I learned about the base64-js from this documentation on mdn .

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM