Codr
Codr

Reputation: 408

How to send binary data in pure JavaScript?

I want to send binary data from a html page to my server via JavaScript, but the server received not the same bytes. The received bytes seems to be converted to a unicode string, see the following example:

xhr.open('POST', '/check', true);
xhr.setRequestHeader('cache-control', 'no-cache');
xhr.setRequestHeader('Content-Type', 'application/octet-stream');
xhr.send('\x41\xFE\x80');

The server should receive 'Aþ€' but it get A├¥┬Ç.

I tested a lot of things like:

//xhr.overrideMimeType('text/plain; charset=iso-8859-1');
//xhr.setRequestHeader('Content-type', 'text/plain; charset=iso-8859-1');
//xhr.setRequestHeader('Content-type', 'application/xml; charset=UTF-8');
//xhr.overrideMimeType('text/plain; charset=x-user-defined');
//xhr.overrideMimeType('text\/plain; charset=x-user-defined');

On server side I run plackup (http://localhost:5000/index.html), and $env->{'CONTENT_LENGTH'} is 5, so the server really seems to get the 5 bytes A݀.

Any hint how to receive the original binary data would be great.

Upvotes: 4

Views: 3703

Answers (1)

Amadan
Amadan

Reputation: 198324

IMHO my Javascript is correct

It is not. As I hinted in a comment, use Uint8Array, not strings, for binary data:

xhr.send(new Uint8Array([0x41, 0xFE, 0x80]));

If you already have a string... this should work:

xhr.send(new Uint8Array(Array.from('\x41\xFE\x80').map(x => x.charCodeAt(0)))

The explanation: The spec for send says:

If body is a Document, then set request body to body, serialized, converted to Unicode, and UTF-8 encoded.

Otherwise, set request body and extractedContentType to the result of extracting body.

String is not a Document, therefore the first option does not apply. The definition of "extracting" is found in the fetch spec:

USVString:

Set action to an action that runs UTF-8 encode on object.

Set Content-Type to text/plain;charset=UTF-8.

Set source to object.

And you can see how UTF-8 encoding of your string looks like:

new TextEncoder().encode('\x41\xFE\x80')
// => Uint8Array(5) [65, 195, 190, 194, 128]

Upvotes: 2

Related Questions