Search code examples
springstompspring-websocketstompjs

Sending binary data stompClient to Spring-websocket controller and consuming


I want to send binary data from my stompClient to the spring controller. this is my JS

var socket = new SockJS('/test/binary');
        var stompClient = Stomp.over(socket);

        socket.binaryType = "arraybuffer";
        var appModel = new ApplicationModel(stompClient);
        ko.applyBindings(appModel);

        appModel.connect();
        appModel.pushNotification("Notifications.");


 var ctx =   canvas.get()[0].getContext('2d');
 ctx.drawImage(img, 0, 0, 320, 240);
 var data  = canvas.get()[0].toDataURL('image/jpeg', 1.0);
 newblob = dataURItoBlob(data);


 stompClient.send("/app/vid", {},  newblob);

function dataURItoBlob(dataURI) {
    // convert base64/URLEncoded data component to raw binary data held in a string
    var byteString;
    if (dataURI.split(',')[0].indexOf('base64') >= 0)
        byteString = atob(dataURI.split(',')[1]);
        else
        byteString = unescape(dataURI.split(',')[1]);

    // separate out the mime component
    var mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0];

    // write the bytes of the string to a typed array
    var ia = new Uint8Array(byteString.length);
    for (var i = 0; i < byteString.length; i++) {
        ia[i] = byteString.charCodeAt(i);
    }

   return new Blob([ia], {type:mimeString});

I have tried sending Blob, Uint8Array the message is sent but on the server side I can not use it. My method in the controller is:

@MessageMapping("/vid")
public void getVid(Message<byte[]> message, Principal principal) throws IOException {
ByteArrayOutputStream bOut = new ByteArrayOutputStream();
bOut.write(message.getPayload());
bOut.flush();
byte[]  imageInByte = bOut.toByteArray();
bOut.close();

InputStream in = new ByteArrayInputStream(imageInByte);
BufferedImage bImageFromConvert = ImageIO.read(in);

ImageIO.write(bImageFromConvert, "jpeg", new File(
        "E:/new-darksouls.jpg"));

template.convertAndSend("/topic/binarydemo2",   message);

I have used ByteArrayMessageConverter,StringMessageConverter,MappingJackson2MessageConverter and a Base64JavaObjectMessageConverter that I wrote:

public class Base64JavaObjectMessageConverter extends AbstractMessageConverter {

public Base64JavaObjectMessageConverter() {
    super(MimeTypeUtils.APPLICATION_OCTET_STREAM);
}

@Override
protected boolean supports(Class<?> clazz) {
    return true;
}


@Override
public Object convertFromInternal(Message<?> message, Class<?> targetClass) {
    byte[] messageBytes =Base64Utils.decode( (byte[])message.getPayload() );
    return SerializationUtils.deserialize( messageBytes  );
}

@Override
public Object convertToInternal(Object payload, MessageHeaders headers) {
    byte[] messageBytes = SerializationUtils.serialize( payload );
    return Base64Utils.encode( messageBytes);
}

}

I only am able to send the byte[] as string removing the 'base64' etc from it:

stompClient.send("/app/vid", {},   data.split(',')[1]);

in the controller :

@MessageMapping("/vid")
public void getVid(String message, Principal principal) throws IOException {


    byte[] bytearray = Base64.decode(message);
    BufferedImage imag=ImageIO.read(new ByteArrayInputStream(bytearray));

    ImageIO.write(imag, "jpg", new File("E:/new-darksouls.jpg"));

But this is not what I wish to achieve. I suppose that this will take a tall on performance.

I am using Spring 4.2.0.RC1 WebSockets,StompJS I read on different posts that it is possible to send from back end to client and client to back end but I was not able to reproduce it. I would be very thankful if I can get a concrete example how to structure and send the Uint8Array or blob on the client and how to deal with it on the server side.

Thank you for your time and help


Solution

  • SockJS does not support sending binary data. You can send binary messages with use STOMP over a plain WebSocket.

    On the server side see StompSubProtocolHandler which checks if incoming messages are binary or text and hence can handle either. From there it doesn't matter much, i.e. from a STOMP perspective the body is a byte array and the content-type/content-length headers determine what the body contains and how it should be intepreted. On the sending side StompSubProtocolHandler can also send either binary or text and at the moment it uses binary if the content-type is application/octet-stream and as long as SockJS is not being used.

    So in short over WebSocket it should work but not when using SockJS as the transport.