5

Some context: I'm trying to upload files to Sharepoint through Salesforce. The file is read clientside with a FileReader and its readAsArrayBuffer method, the result is converted to a Uint8Array, which is then passed onto my apex controller. There im reading the char (Integer) array and using that to create a Blob instance to send to Sharepoint. Sharepoint then returns the amount of bytes that has been uploaded and here is where it goes wrong. The amount of bytes are incorrect.

The bug.. or my ignorance (most likely): I've created a short script to showcase the results and what im expecting:

// Test
String rangeTo127 = String.fromCharArray(new List<Integer>{100});
String rangeTo255 = String.fromCharArray(new List<Integer>{200});
String rangeTominus127to127 = String.fromCharArray(new List<Integer>{-100});

System.assertEquals(1, Blob.valueOf(rangeTo127).size()); // is 1
System.assertEquals(1, Blob.valueOf(rangeTo255).size()); // is 2! Why? (maybe because its a unsigned char)
System.assertEquals(1, Blob.valueOf(rangeTominus127to127).size()); // is 3! Why? (so not a signed or unsigned char... am i even in the right direction?)

// Actual icon .png where we know the actual size is 291 bytes
List<Integer> chunk = new List<Integer>{137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,16,0,0,0,16,4,3,0,0,0,237,221,226,82,0,0,0,3,115,66,73,84,8,8,8,219,225,79,224,0,0,0,21,80,76,84,69,0,0,0,216,152,16,248,208,176,104,56,8,224,80,96,248,208,32,255,255,255,141,248,238,129,0,0,0,7,116,82,78,83,255,255,255,255,255,255,0,26,75,3,70,0,0,0,9,112,72,89,115,0,0,10,240,0,0,10,240,1,66,172,52,152,0,0,0,32,116,69,88,116,83,111,102,116,119,97,114,101,0,77,97,99,114,111,109,101,100,105,97,32,70,105,114,101,119,111,114,107,115,32,77,88,187,145,42,36,0,0,0,102,73,68,65,84,120,156,101,205,193,9,128,48,12,5,208,80,177,11,136,247,82,241,174,150,116,130,12,96,13,185,23,10,217,127,4,219,224,205,92,254,131,255,33,160,223,193,31,213,8,30,170,243,170,53,116,196,41,165,35,122,80,64,17,238,161,77,92,144,60,64,46,208,192,69,49,210,214,129,248,236,204,163,42,0,171,109,228,158,109,172,231,90,22,251,133,140,108,104,185,101,213,23,167,84,36,237,0,150,218,39,0,0,0,0,73,69,78,68,174,66,96,130};
System.assertEquals(291, Blob.valueOf(String.fromCharArray(chunk)).size()); // is 382! Why?

Question: What do i need to do to ensure that the Blob instance is giving me the results i expect (in this case 291 bytes for the above image)?

I've tried to send the data as raw text as well (using reader.readAsText or iterating over the Uint array to get its binary) but once that is converted to a Blob i get the exact same size (382). If anyone could point my error, it would be greatly appreciated!

1 Answer 1

5

Strings in Apex Code are always UTF-8. When you convert an array using String.fromCharArray, you're creating a UTF-8 compatible string. 200 in UTF-8 is represented by the hex value 0xC388 (binary 1100 0011 1000 1000). Similarly, -100 becomes 0xEFBE9C (1110 1111 1011 1110 1001 1100). All of this is because of how UTF-8 handles values outside 0-127. Starting at 128, continuation bytes are required to represent a character; negative numbers are really big unsigned numbers, starting at 231 and continuing through to 232-1.

To get a correct value to begin with, you will want to use base64 encoding and pass that in instead:

public static void sendFile(String base64EncodedFile) {
  Blob payload = EncodingUtil.base64Decode(base64EncodedFile);
  ...

See this answer on Stack Overflow for how to convert a Uint8Array to a base64 encoded string.

1
  • Thank you for the clear explanation and the solution! It finally works. Commented Jun 21, 2019 at 18:53

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .