Enviando archivos por WebSocket

Si describimos en forma simple a la tecnología WebSocket se puede decir que permite el intercambio de información entre un navegador web y un servidor web en forma bidireccional en cualquier momento. Es decir, una vez que se establece la conexión, cualquiera de los 2 extremos puede enviar datos en cualquier momento.

Envío de datos en formato de texto

Para enviar cadenas de texto por WebSocket se debe usar el método send que provee la clase WebSocket en JavaScript. Para poder realizar una prueba real, usaremos un servidor de WebSocket público que nos brinda websocket.org.

var url = "wss://echo.websocket.org";
var cadenaDeTexto = "mensaje a enviar";
var socket = new WebSocket(url);
socket.send(cadenaDeTexto);

El servidor de websocket.org retorna todo lo que uno le envía, entonces, en el anterior ejemplo nos retornaría el string "mensaje a enviar". Para poder recibir lo que nos envía un servidor debemos escuchar al evento de recepción de mensaje (método onmessage). El código quedaría así:

var url = "wss://echo.websocket.org";
var cadenaDeTexto = "mensaje a enviar";
var socket = new WebSocket(url);
socket.onmessage = function(event) {
    console.log(event.data);
};
socket.send(cadenaDeTexto);

Podés realizar esta prueba desde la consola de tu navegador.

Envío de archivos

Ahora bien, si quisiéramos enviar un archivo por WebSocket, existen al menos 2 alternativas que se pueden implementar:

Convertir el archivo a Base 64 y enviar los caracteres en Unicode, o bien, enviar directamente el archivo en binario.

Para llevar a cabo la primera opción, se debería convertir el contenido del archivo a base64:

function convertirABase64 (archivo, callbackExito) {
  var lectorArchivo = new FileReader();
  lectorArchivo.onload = function() {
        var base64 = lectorArchivo.result.split(",")[1];
        callbackExito(base64);
  };
  lectorArchivo.readAsDataURL(archivo);
}
convertirABase64(
  document.getElementById("input-file"),
  function(base64AEnviar) {

      var url = "ws://localhost:8000";
     var socket = new WebSocket(url);
     socket.send(base64AEnviar);
  }
);

Entonces en el anterior código, se espera hasta que el archivo se convierta a base 64 para luego enviarlo por WebSocket. Esto implica una demora adicional debido al procedimiento de conversión. También se incrementa el tamaño del archivo enviado en un 33%. La conversión de datos binarios a base 64 implica este incremento adicional.

En cambio, si implementamos la segunda opción, y enviamos el archivo en formato binario, los 2 problemas anteriores estarán solucionados. Para ello, tenemos que indicar al WebSocket que se enviarán datos binarios, por ejemplo:

function readFile(event) {
  var blob = event.target.result;
  var url = "ws://localhost:8000";
  var socket = new WebSocket(url);
  socket.binaryType = "blob";
  socket.send(blob);
}

var reader = new FileReader();
reader.addEventListener('load', readFile);
var file = document.getElementById("input-file");
reader.readAsArrayBuffer(file);

Bibliografía

  1. https://www.html5rocks.com/es/tutorials/websockets/basics/
  2. https://developer.mozilla.org/en-US/docs/Web/API/Blob
  3. https://tools.ietf.org/html/rfc6455
  4. https://www.w3.org/TR/websockets/
  5. https://www.websocket.org/echo.html