2

I'm using jpeg_camera library to get a snapshot from my webapp on my laptop and, by a fetch call, send it to my controller.

snapshot.get_image_data returns an object with 3 properties (data: Uint8ClampedArray, width, heigh).

When I do the fetch call sendPic(data) I always get a 400 Error, because the ModelState is not valid.
That means Byte[] is not good for Uint8ClampedArray from JS.

What is the equivalent for that object?

I've found also a method that return a base64 and I can convert it inside the controller into a Byte[] but I'd like to avoid this solution.

the JS code:

function savePic() {
    var test1 = snapshot.get_image_data((done) => {
        var data = {
            "Pic": done.data,
            "IdActivity": idActivity,
            "Instant": new Date().toISOString()
        };
        sendPic(data);
    });
}

function sendPic(data) {
    fetch(uriPicsEndPoint, {
        body: JSON.stringify(data),
        headers: {
            "Content-Type": "application/json; charset=utf-8"
        },
        credentials: 'include',
        method: 'POST'
    });
}

The API Controller:

[Authorize]
[HttpPost]
public async Task<IActionResult> SavePic([FromBody] Selfie selfie)
{
    if (ModelState.IsValid)
    {
        try
        {
            var storageAccount = CloudStorageAccount.Parse(_configuration["ConnectionStrings:Storage"]);
            var blobClient = storageAccount.CreateCloudBlobClient();
            var camerasContainer = blobClient.GetContainerReference("selfies");
            await camerasContainer.CreateIfNotExistsAsync();
            var id = Guid.NewGuid();
            var fileExtension = ".jpeg";
            var blobName = $"{selfie.IdActivity}/{id}{fileExtension}";
            var blobRef = camerasContainer.GetBlockBlobReference(blobName);

            await blobRef.UploadFromByteArrayAsync(selfie.Pic, 0, selfie.Pic.Length);
            string sas = blobRef.GetSharedAccessSignature(
                new SharedAccessBlobPolicy()
                {
                    Permissions = SharedAccessBlobPermissions.Read
                });
            var blobUri = $"{blobRef.Uri.AbsoluteUri}{sas}";

            var notification = new UpdateSelfieRequest()
            {
                UriPic = blobUri,
                IdActivity = selfie.IdActivity,
                Instant = selfie.Instant
            };

            string serviceBusConnectionString = _configuration["ConnectionStrings:ServiceBus"];
            string queueName = _configuration["ServiceBusQueueName"];
            IQueueClient queueClient = new QueueClient(serviceBusConnectionString, queueName);
            var messageBody = JsonConvert.SerializeObject(notification);
            var message = new Message(Encoding.UTF8.GetBytes(messageBody));
            await queueClient.SendAsync(message);
            await queueClient.CloseAsync();

            return Ok();
        }
        catch
        {
            return StatusCode(500);
        }
    }
    else
    {
        return BadRequest();
    }
}

And the CLASS "Selfie":

public class Selfie
{
    public Byte[] Pic { get; set; }
    public int IdActivity { get; set; }
    public DateTime Instant { get; set; }
}
trashr0x
  • 6,457
  • 2
  • 29
  • 39
  • 1
    Can you post what's actually being sent? You can see that in the Network tab of the Developer Tools in most browsers – Camilo Terevinto Jul 12 '18 at 21:54
  • Well, [Uint8ClampedArray](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8ClampedArray) is an 8 Bit Unsigned Integer (0-255), like a [Byte type](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/byte). – Jimi Jul 12 '18 at 22:12
  • As a quick guess, if you're using `JSON.stringify` then the parameter should be type JObject? – Mark G Jul 12 '18 at 22:22
  • @Mark G [JSON.stringify()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify) should be a JSON string in the form of an array of byte values (in relation to the `Pic` member). `JSON.Deserialize` should get it right. – Jimi Jul 12 '18 at 22:33
  • @Jimi But with model binding the deserialization is done for you. My suggestion was to define parameter as `[FromBody] JObject selfie` so you could see the raw data (like Pic as an array of byte values) which could be converted by using `selfie["Pic"].Values()`. – Mark G Jul 12 '18 at 23:01
  • @Mark G Ah yes, of course. You're right. – Jimi Jul 12 '18 at 23:36
  • I will post the screenshot of the object sent from dev tools: [link](https://ibb.co/f6k9E8) – Alessandro DM Jul 13 '18 at 08:07

1 Answers1

0

When you use stringify it becomes {"0":255, "1":0, "2":0, "3":255, ...} in the JSON.

If you have your action parameter defined as JObject selfie you could get bytes using var pic = selfie["Pic"].ToObject<Dictionary<int, byte>>().

Alternatively you could post data as form values and define action as SavePic(Dictionary<int, byte> Pic, int IdActivity, DateTime Instant) using the following code in browser to test:

var ctx = document.createElement('canvas').getContext('2d');
ctx.fillStyle = 'red';
ctx.fillRect(5, 5, 5, 5);

$.post('SavePic', {
    Pic: ctx.getImageData(5, 5, 5, 5).data,
    IdActivity: 1,
    Instant: new Date().toISOString()
});

However it may be easier to convert the Uint8ClampedArray to regular array before posting data.

Mark G
  • 2,848
  • 1
  • 24
  • 32