Welcome folks today in this blog post we will be building a simple p2p
file sharing project in react.js
and node.js and express using socket.io
and simple-peer
in browser using javascript. All the full source code of the application is shown below.
LIVE DEMO
https://p2pfiletransfer.herokuapp.com/
Get Started
In order to get started you need to make a new p2p
folder and inside it we will be making the backend
and the frontend of the application
mkdir p2p
cd p2p
And now we need to initialize a node.js project using the below command
npm init -y
Now we need to install the express
and the socket.io
libraries using the below command
npm i express
npm i socket.io
And now you need to make the index.js
file and copy paste the following code
index.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 |
const express = require("express"); const http = require("http"); const app = express(); const path = require('path') const server = http.createServer(app); const socket = require("socket.io"); const io = socket(server); const users = {}; const socketToRoom = {}; io.on("connection", (socket) => { socket.on("join room", (roomID) => { if (users[roomID]) { const length = users[roomID].length; if (length === 2) { socket.emit("room full"); return; } users[roomID].push(socket.id); } else { users[roomID] = [socket.id]; console.log(users); console.log(users[roomID]); } socketToRoom[socket.id] = roomID; console.log("dsd" + socketToRoom); console.log("sfsfsdf" + socketToRoom[socket.id]); const usersInThisRoom = users[roomID].filter((id) => id !== socket.id); socket.emit("all users", usersInThisRoom); }); socket.on("sending signal", (payload) => { io.to(payload.userToSignal).emit("user joined", { signal: payload.signal, callerID: payload.callerID, }); }); socket.on("returning signal", (payload) => { io.to(payload.callerID).emit("receiving returned signal", { signal: payload.signal, id: socket.id, }); }); socket.on("disconnect", () => { const roomID = socketToRoom[socket.id]; let room = users[roomID]; if (room) { room = room.filter((id) => id !== socket.id); users[roomID] = room; socket.broadcast.emit("user left", socket.id); } }); }); server.listen(process.env.PORT || 8000, () => console.log("server is running on port 8000") ); |
As you can see we are starting a basic express
app and then we are passing it to the socket.io
server and then we have the various events where we listen for connection of the socket
and the disconnection
of the socket.
Now we need to make the client
folder and inside it we will be making the react.js
app as shown below
cd client
npm create vite@ p2pclient
cd p2pclient
npm i
Now we need to install the below dependencies
as shown below
npm i react-bootstrap bootstrap
npm i streamsaver
npm i simple-peer
npm i uuid
npm i socket.io-client
npm i react-router-dom
npm i buffer
And now you will see the below directory
structure of the react.js app as shown below
And now we need to go to App.js
file and copy paste the following code
App.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
import React from 'react'; import { BrowserRouter, Route, Switch } from "react-router-dom"; import CreateRoom from "./routes/CreateRoom"; import Room from "./routes/Room"; function App() { return ( <BrowserRouter> <Switch> <Route path="/p2p" exact component={CreateRoom} /> <Route path="/p2p/room/:roomID" component={Room} /> </Switch> </BrowserRouter> ); } export default App; |
As you can see in the above code we are writing the various routes
for our application. And for this we are using the browserRouter and inside it we are defining the routes
and inside it we are loading the various components.
And now we need to define the routes
folder and inside it we need to define the below files
routes/CreateRoom.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
import React from "react"; import { v1 as uuid } from "uuid"; import 'bootstrap/dist/css/bootstrap.min.css'; import { Button,Container } from 'react-bootstrap'; const CreateRoom = (props) => { function create() { const id = uuid(); props.history.push(`/p2p/room/${id}`); } return ( <Container className="text-center"> <br/><br/> <h1>P2P File Transfer</h1> <br/><br/> <Button className="btn btn-danger" onClick={create}>Create room</Button> </Container> ); }; export default CreateRoom; |
As you can see this will be the page which will be shown to the user once user goes to the /
homepage and we have the simple button here to create the room
here as shown below
And now we need to define the Room.js
file and copy paste the following code
routes/Room.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 |
import React, { useEffect, useRef, useState } from "react"; import io from "socket.io-client"; import Peer from "simple-peer"; import streamSaver from "streamsaver"; import "bootstrap/dist/css/bootstrap.min.css"; import { Button, Container, Spinner, Form } from "react-bootstrap"; import { useHistory } from "react-router-dom"; const worker = new Worker("../worker.js"); const Room = (props) => { const [connectionEstablished, setConnection] = useState(false); const [file, setFile] = useState(); const [gotFile, setGotFile] = useState(false); const history = useHistory(); const socketRef = useRef(); const peerRef = useRef(); const fileNameRef = useRef(""); const roomID = props.match.params.roomID; useEffect(() => { socketRef.current = io.connect("/"); socketRef.current.emit("join room", roomID); socketRef.current.on("all users", (users) => { peerRef.current = createPeer(users[0], socketRef.current.id); }); socketRef.current.on("user joined", (payload) => { peerRef.current = addPeer(payload.signal, payload.callerID); }); socketRef.current.on("receiving returned signal", (payload) => { peerRef.current.signal(payload.signal); setConnection(true); }); socketRef.current.on("room full", () => { alert("room is full"); history.goBack(); }); }, []); function createPeer(userToSignal, callerID) { const peer = new Peer({ initiator: true, trickle: false, }); peer.on("signal", (signal) => { socketRef.current.emit("sending signal", { userToSignal, callerID, signal, }); }); peer.on("data", handleReceivingData); return peer; } function addPeer(incomingSignal, callerID) { const peer = new Peer({ initiator: false, trickle: false, }); peer.on("signal", (signal) => { socketRef.current.emit("returning signal", { signal, callerID }); }); peer.on("data", handleReceivingData); peer.signal(incomingSignal); setConnection(true); return peer; } function handleReceivingData(data) { if (data.toString().includes("done")) { setGotFile(true); const parsed = JSON.parse(data); fileNameRef.current = parsed.fileName; } else { worker.postMessage(data); } } function download() { setGotFile(false); worker.postMessage("download"); worker.addEventListener("message", (event) => { const stream = event.data.stream(); const fileStream = streamSaver.createWriteStream(fileNameRef.current); stream.pipeTo(fileStream); }); } function selectFile(e) { setFile(e.target.files[0]); } function sendFile() { const peer = peerRef.current; const stream = file.stream(); console.log(stream); const reader = stream.getReader(); console.log(reader); reader.read().then((obj) => { handlereading(obj.done, obj.value); }); function handlereading(done, value) { if (done) { peer.write(JSON.stringify({ done: true, fileName: file.name })); return; } peer.write(value); reader.read().then((obj) => { handlereading(obj.done, obj.value); }); } } let copy = () => { let text = document.getElementById("text"); text.select(); document.execCommand("copy"); }; let body; if (connectionEstablished) { body = ( <Container className="text-center"> <br /> <br /> <h1>Connected With Peer (You can Share or Transfer Files)</h1> <br/><br/> <Form> <Form.Label>Select the File:</Form.Label> <Form.Control size="lg" onChange={selectFile} type="file" ></Form.Control> </Form> <br/> <Button onClick={sendFile}>Send file</Button> </Container> ); } else { body = ( <Container className="text-center"> <br /> <br /> <br /> <h1>Waiting for the Peer to Join</h1> <br /> <br /> <Spinner animation="grow" variant="primary" /> <Spinner animation="grow" variant="success" /> <Spinner animation="grow" variant="danger" /> <br /> <br /> <Form> <Form.Group> <Form.Label>Your Room URL: (Share with Anyone in World)</Form.Label> <br /> <br /> <Form.Control size="lg" id="text" type="text" value={window.location.href} readOnly /> </Form.Group> <br /> <Button onClick={copy} variant="primary"> Copy to Clipboard </Button> </Form> </Container> ); } let downloadPrompt; if (gotFile) { downloadPrompt = ( <Container className="text-center"> <br/><br/> <h3> You have received a file. Would you like to download the file? </h3> <br/> <Button onClick={download}>Yes</Button> </Container> ); } return ( <Container> {body} {downloadPrompt} </Container> ); }; export default Room; |
As you can see we are connecting the two
sockets with each other first of all we need to create the room and share the link with other one and as soon as the other peer joins it will automatically give you the html
form where you can transfer the files
over the p2p data channel and now we need to define the worker.js
file inside the public folder as shown below
public/worker.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
let array = []; self.addEventListener("message", event => { console.log(event.data) if (event.data === "download") { console.log(array) const blob = new Blob(array); console.log(blob) self.postMessage(blob); array = []; } else { console.log("else" + event.data) array.push(event.data); } }) |
As you can see inside this file we are just downloading
the file which is transferred over the p2p
data channel as an attachment.
https://github.com/gauti123456/p2pfiletransfer