How to build a file transfer application with node and webRTC

Hello there, In this article, we would be building a file transfer application using webRTC and node.js. This app would let users transfer heavy files to each other without the need for a server.
Note, in order to proceed with the tutorial, I would highly recommend you to please read my previous tutorial on webRTC Build a video chat app with webRTC.
Alright, let's begin.
Setting up the server for signaling.
In this very step, we are going to set up a simple signaling server using node and socket.io. I won't be explaining the signaling process as I have already that in Build a video chat app with webRTC article. If you haven't read that, please go and read.
// signaling server
const http = require('http');
const express = require('express');
const { Server: SocketIO } = require('socket.io');
const app = express();
const server = http.createServer(app);
const io = new SocketIO(server, { cors: true });
const PORT = process.env.PORT || 8000;
app.use(express.static('./public'));
io.on('connection', socket => {
socket.emit('me', socket.id);
socket.on('make:offer', data => {
const { offer, to } = data;
socket.to(to).emit('incomming:offer', { offer, from: socket.id });
});
socket.on('make:answer', data => {
const { answer, to } = data;
socket.to(to).emit('incomming:answer', { answer, from: socket.id });
});
});
server.listen(PORT, () => console.log(`🚀 Server started at PORT${PORT}`));
Very basic, no rocket science. I hope that the above code is clear to you.
Great, now let's move further to our front-end part.
Let's start with a basic boiler plate code
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<h3 id="my-id"></h3>
<form id="connect">
<input type="text" id="id" />
<button type="submit">SEND</button>
</form>
<form id="file-transfer">
<input id="file" type="file" />
<button type="submit">SEND</button>
</form>
<script src="/socket.io/socket.io.js"></script>
</body>
</html>
Pretty simple.
Now let's define some global variables.
const form = document.getElementById('file-transfer');
const connectForm = document.getElementById('connect');
const socket = io();
const peer = new RTCPeerConnection();
let dataChannel;
Alright, good going 🚀
Now let's add some event listeners to our form and socket.
socket.on('me', id => document.getElementById('my-id').innerText = id); // to get our own socket id.
form.addEventListener('submit', async (ev) => {
ev.preventDefault();
});
connectForm.addEventListener('submit', async (ev) => {
ev.preventDefault();
});
Alright, now let's focus on our connection process.
So, what we would be doing is, when a user submits the connection form, we are going to init the signaling process and connect both the peers.
connectForm.addEventListener('submit', async (ev) => {
ev.preventDefault();
const remoteId = document.getElementById('id').value;
const offer = await peer.createOffer();
await peer.setLocalDescription(new RTCSessionDescription(offer));
// This dataChanel we are going to use to send data
dataChannel = peer.createDataChannel('file-transfer');
socket.emit('make:offer', { offer, to: remoteId });
});
and now, create an event listener for the incoming offer
socket.on('incomming:offer', async data => {
await peer.setRemoteDescription(new RTCSessionDescription(data.offer));
const answer = await peer.createAnswer();
await peer.setLocalDescription(new RTCSessionDescription(answer));
socket.emit('make:answer', { answer, to: data.from })
// Init the remote datachannel as soon as the first peer creates a data channel
peer.addEventListener('datachannel', ev => {
dataChannel = ev.channel
dataChannel.send('Hello Peer');
});
})
and finally, the incoming answer
socket.on('incomming:answer', async data => {
await peer.setRemoteDescription(new RTCSessionDescription(data.answer));
})
Now let's complete our file transfer form listener where are actually going to transfer the files.
form.addEventListener('submit', async (ev) => {
ev.preventDefault();
// Get the file from input field.
const file = document.getElementById('file').files[0];
// convert the file to buffer
const fileBuffer = await file.arrayBuffer();
// finally, send it to the peer data channel
dataChannel.send(fileBuffer);
});
Great, so on the receiver side, listen for this file and download the file to the client.
connectForm.addEventListener('submit', async (ev) => {
ev.preventDefault();
const remoteId = document.getElementById('id').value;
const offer = await peer.createOffer();
await peer.setLocalDescription(new RTCSessionDescription(offer));
// This dataChanel we are going to use to send data
dataChannel = peer.createDataChannel('file-transfer');
socket.emit('make:offer', { offer, to: id });
// Adding a message listener
dataChannel.addEventListener('message', ev => {
if (typeof ev.data == 'object') {
const a = document.createElement('a');
const blob = new Blob([ev.data]);
const obj = URL.createObjectURL(blob);
a.href = obj;
a.download = 'rec.png';
a.click()
}
});
});
And that's it, the file would get downloaded to the client's machine.
Comments
Join the discussionThanks for publishing this interesting series. Can you publish the complete code for this project?
Yes sure, can you email me at piyushgarg.dev@gmail.com
WebRTC - Web real-time communications
With WebRTC, you can add real-time communication capabilities to your application that works on top of an open standard.





