Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

nodejs binary websocket mimetype handling

i'm not 100% sure but from what i read when i send a blob (binary data) over websocket, the blob does not contain any file information. (Also the official specification states that wesockets only send the raw binary)

  1. the filesize
  2. the mimetype
  3. user info (explain later)

i'm using https://github.com/websockets/ws

Testing:

Sending directly the blob from an input file.

ws.send(this.files[0]) //this should already contain the info

Creating a new blob with the native javascript api from file setting the proper mimetype.

ws.send(new Blob([this.files[0]],{type:this.files[0].type})); //also this

on both sides you can get only the effective blob without any other information.

Is it possible to append let's say a 4kb predefined json data converted also to binary that contains important information like the mimetype and the filesize, and then just split off the 4kb when needed?

{"mime":"txt/plain","size":345}____________4KB_REST_OF_THE_BINARY

OR

ws.send({"mime":"txt\/plain","size":345})
ws.send(this.files[0])

Even if the first one is the worst solution ever it would allow me to send everything in one time.

The second one has a big problem:

it's a chat that allows to send also files like documents,images,music videos.

i could write some sort of handshaking system when sending the file/user info before i send the binary data.

BUT

if another person sends also a file, as it's async, the handshaking system has no chance to determine wich file is the right one for the correct user and mimetype.

So how do you properly send a binary file in a multiuser async envoirement?

i know i can convert to base64 but thats 30% bigger.

btw. Totally disappointed with Apple... while chrome shows every binary data properly, my ios devices are not able to handle blob's, only images will show in blob or base64 format, not even a simple txt file. Basically only a <img> tag can read dynamic files.

How everything works (now):

  1. user sends a file
  2. nodejs gets the binary data, also user info... but not mimetype,filename,size.
  3. nodejs broadcasts the raw binary file to all the users.(can't specify user & file info)
  4. clients create a bloburl (who send that? XD).

EDIT

what i have now:

client 1 (sends a file)CHROME

fileInput.addEventListener('change',function(e){
 var file=this.files[0];
 ws.send(new Blob([file],{
  type:file.type //<- SET MIMETYPE
 }));
 //file.size
},false);

note: file is already a blob ... but this is how you would normally create a new blob specifying the mimetype.

server (broadcasts the binary data to the other clients)NODEJS

aaaaaand the mimetype is gone...

ws.addListener('message',function(binary){
 var b=0,c=wss.clients.length;
 while(b<c){
  wss.clients[b++].send(binary)
 }
});

client 2 (recieves the binary)CHROME

ws.addEventListener('message',function(msg){
 var blob=new Blob([msg.data],{
      type:'application/octet-stream' //<- LOST
     });
 var file=window.URL.createObjectURL(blob);
},false);

note: m.data is already a blob ... but this is how you would normally create a new blob specifying the mimetype witch is lost.

In client 2 i need the mimetype and naturally i also need the info about the user, wich can be retrieved from client 1 or the server (not a good choice)...

like image 780
cocco Avatar asked Apr 02 '15 14:04

cocco


People also ask

Can a single WebSocket handle text and binary data?

WebSocket enables bidirectional, message-oriented streaming of text and binary data between client and server.

Is Node js good for WebSockets?

js comes in. Node. js can maintain many hundreds of WebSockets connections simultaneously. WebSockets on the server can become complicated as the connection upgrade from HTTP to WebSockets requires handling.

What is binary data in JavaScript?

Binary data in JavaScript is implemented in a non-standard way, compared to other languages. But when we sort things out, everything becomes fairly simple. The basic binary object is ArrayBuffer – a reference to a fixed-length contiguous memory area.

What is WebSocket in node js?

Web Socket is a protocol that provides full-duplex(multiway) communication i.e allows communication in both directions simultaneously. It is a modern web technology in which there is a continuous connection between the user's browser(client) and the server.


1 Answers

You're a bit out of luck with this because Node doesn't support the Blob interface and so any data you send or receive in Binary with Node is just Binary. You would have to have something that knew how to interpret a Blob object.

Here's an idea, and let me know if this works. Reading through the documentation for websockets\ws it says it supports sending and receiving ArrayBuffers. Which means you can use TypedArrays.

Here's where it gets nasty. You set a certain fixed n number of bytes at the beginning of every TypedArray to signal the mime type encoded in utf8 or what have you, and the rest of your TypedArray contains your file's bytes.

I would recommend using UInt8Array because utf8 characters are 8 bits long and your text will probably be readable when encoded that way. As for the file bits you'll probably just end up writing those down somewhere and appending an ending to it.

Also note, this method of interpretation works both ways whether from Node or in the Browser.

This solution is really just a form of type casting and you might get some unexpected results. The fixed length of your mime type field is crucial.

Here it is illustrated. Copy, paste, set the image file to whatever you want and then run that. You'll see the mime type I set pop out.

var fs = require('fs');


//https://stackoverflow.com/questions/8609289/convert-a-binary-nodejs-buffer-to-javascript-arraybuffer
function toUint8Array(buffer) {
  var ab = new ArrayBuffer(buffer.length);
  var array = new Uint8Array(ab);

  for (var i = 0; i < buffer.length; ++i) {
    array[i] = buffer[i];
  }

  return array;
}

//data is a raw Buffer object
fs.readFile('./ducklings.png', function (err, data) {
  var mime = new Buffer('image/png');
  var allBuffed = Buffer.concat([mime, data]);
  var array = toUint8Array(allBuffed);
  var mimeBytes = array.subarray(0,9); //number of characters in mime Buffer
  console.log(String.fromCharCode.apply(null, mimeBytes));
});

Here's how you do it on the client side:

SOLUTION A: GET A PACKAGE

Get buffer, an implementation of Node's Buffer API for browsers. The solution to concatenate Byte buffers will work exactly as before. You can append fields like To: and what not as well. The way you format your headers in order to best serve your clients will be an evolving process I'm sure.

SOLUTION B: OLD SCHOOL

STEP 1: Convert your Blob to an ArrayBuffer

Notes: How to convert a String to an ArrayBuffer

var fr = new FileReader();
fr.addEventListener('loadend', function () {
//Asynchronous action in part 2.
  var message = concatenateBuffers(headerStringAsBuffer, fr.result);
  ws.send(message);
});
fr.readAsArrayBuffer(blob);

STEP 2: Concatenate ArrayBuffers

function concatenateBuffers(buffA, buffB) {
  var byteLength = buffA.byteLength + buffB.byteLength;
  var resultBuffer = new ArrayBuffer(byteLength);
  //wrap ArrayBuffer in a typedArray/view
  var resultView = new Uint8Array(resultBuffer);
  var viewA = new Uint8Array(resultBuffer);
  var viewB = new Uint8Array(resultBuffer);
  //Copy 8 bit integers AKA Bytes
  resultView.set(viewA);
  resultView.set(viewB, viewA.byteLength);
  return resultView.buffer
}

STEP 3: Receive and Reblob

I'm not going to repeat how to convert the concatenated String bytes back into a string because I've done it in the server example, but for turning the file bytes into a blob of your mime type is fairly simple.

new Blob(buffer.slice(offset, buffer.byteLength), {type: mimetype});

This Gist by robnyman goes into further details on how you would use an image transmitted via XHR, put it into localstorage, and use it in an image tag on your page.

like image 189
Breedly Avatar answered Oct 19 '22 08:10

Breedly