7

I've been beating my head over this and I can't find a proper solution. I want to be able to upload images to the server via socket.io emit and save them to a MongoDB database later. How do I do this? I've seen people doing it with base64 encoding but I can't figure out how that exactly works, there are other questions on this website asking about sending an image to client from server via socket.io but none about this. All help is appreciated. <3

Goal: To upload an image to server with socket.emit('image', someimagefile) or similar.

I'd really appreciate if you provide a similar way to send an image to the client.

MasOOd.KamYab
  • 944
  • 11
  • 25
  • 6
    Don't use base64... it's pointless. All you're doing is adding 33% overhead to the size, wasting CPU, memory usage, for zero benefit. Web Sockets and Socket.IO support binary transfer. Additionally, why would you want to send it over socket.IO? A normal HTTP request will work fine. Also, while you can shove that binary data in your MongoDB database, it's rarely the best solution. Better to just store the asset on disk so you can serve it up using normal CDNs and what not later. – Brad Dec 25 '19 at 19:22
  • See also [socket io, node js, Simple example to send image/files from server to client](https://stackoverflow.com/questions/26331787/socket-io-node-js-simple-example-to-send-image-files-from-server-to-client) – ggorlen Mar 11 '21 at 20:38

3 Answers3

10

As you mentioned, you can convert the image to base64 using FileReader.readAsDataURL and send the encoded string, and decode it on the server:

document.getElementById('file').addEventListener('change', function() {

  const reader = new FileReader();
  reader.onload = function() {
    const base64 = this.result.replace(/.*base64,/, '');
    socket.emit('image', base64);
  };
  reader.readAsDataURL(this.files[0]);

}, false);
socket.on('image', async image => {
    const buffer = Buffer.from(image, 'base64');
    await fs.writeFile('/tmp/image', buffer).catch(console.error); // fs.promises
});

Or better use FileReader.readAsArrayBuffer to get an array of bytes that you'll send to the server.

document.getElementById('file').addEventListener('change', function() {

  const reader = new FileReader();
  reader.onload = function() {
    const bytes = new Uint8Array(this.result);
    socket.emit('image', bytes);
  };
  reader.readAsArrayBuffer(this.files[0]);

}, false);
socket.on('image', async image => {
    // image is an array of bytes
    const buffer = Buffer.from(image);
    await fs.writeFile('/tmp/image', buffer).catch(console.error); // fs.promises
});

To receive from the server:

// Server side
socket.emit('image', image.toString('base64')); // image should be a buffer
// Client side
socket.on('image', image => {
    // create image with
    const img = new Image();
    // change image type to whatever you use, or detect it in the backend 
    // and send it if you support multiple extensions
    img.src = `data:image/jpg;base64,${image}`; 
    // Insert it into the DOM
});
Marcos Casagrande
  • 37,983
  • 8
  • 84
  • 98
1

Base64 can work, but one more thing to keep in mind is that socket buffer size limit is 1 MB. (This can be increased according to docs).

So I guess if the file size is huge, its better to stream it with something like socket.io-stream

Sanjay
  • 551
  • 6
  • 14
0

i don't know if any one is looking for it anymore but I made possible to send media via socket.io... here is the code:

// sending media from client side
$("#send_media").change(function (e) {
  var data = e.originalEvent.target.files[0];
  var reader = new FileReader();
  reader.onload = function (evt) {
    var msg = {};
    msg.file = evt.target.result;
    msg.fileName = data.name;
    socket.emit("base64 file", msg);
    console.log(msg)
  };
  reader.readAsDataURL(data);
});
   
// showing media to ui 
socket.on("base64 image", (msg) => {
  console.log("as", msg);
  $(".messages")
    .append(`<img src=${msg.file} alt="Red dot" />`);

  scrollToBottom();
});

// sending media from server side
    socket.on("base64 file", function (msg) {
      console.log("received base64 file from server: " + msg.fileName);
      socket.username = msg.username;
      io.to(roomId).emit('base64 image', //exclude sender
      // io.sockets.emit(
      //   "base64 file", //include sender

        {
          file: msg.file,
          fileName: msg.fileName,
        }
      );
    });