DEV Community

Cover image for Building a Real-Time Peer-to-Peer File Sharing Web App with WebSockets and Pako Compression
abhilaksh-arora
abhilaksh-arora

Posted on

Building a Real-Time Peer-to-Peer File Sharing Web App with WebSockets and Pako Compression

Introduction

In today’s digital world, real-time file sharing has become essential. Services like AirDrop allow seamless peer-to-peer transfers, but implementing such a system over the web requires careful design choices. In this blog, I will walk you through my approach to building a real-time, WebSocket-based file-sharing system that supports compressed file transfers using Pako.js.

This project enables users to join a room, discover peers, and exchange files efficiently over WebSockets. The added advantage of Pako compression reduces network bandwidth usage, making transfers faster and more efficient.

Deployed URL-: https://intrashare.abhilaksharora.com/

Project Architecture & Tech Stack

Frontend:

  • Next.js – For a modern and scalable frontend.
  • Tailwind CSS – For styling.
  • Pako.js – To handle file compression and decompression.
  • WebSockets – For real-time communication.

Backend:

  • Node.js – Server-side JavaScript runtime.
  • WebSocket Server (ws package) – To manage real-time connections and room-based messaging.
  • Express.js (Optional) – If we extend it with REST APIs.

Frontend Implementation

1. Establishing a WebSocket Connection

We need to initialize a WebSocket connection that allows users to join a room and communicate.

Extracting Room Code from URL

useEffect(() => { if (typeof window !== "undefined") { const params = new URLSearchParams(window.location.search); const roomCodeFromURL = params.get("roomCode"); if (roomCodeFromURL) setRoomCode(roomCodeFromURL); } }, []); 
Enter fullscreen mode Exit fullscreen mode

This extracts the roomCode parameter from the URL (e.g., ?roomCode=ABCDE) and updates the state.

Joining a WebSocket Room

const joinRoom = (roomCode: string) => { if (!roomCode.trim()) { alert("Please enter a room code!"); return; } if (myNameRef.current === "") { const name = randomName(); setMyName(name); myNameRef.current = name; } const socket = new WebSocket("wss://file-transfer-server.com"); setWs(socket); socket.onopen = () => { socket.send(JSON.stringify({ type: "join_room", roomCode, name: myNameRef.current })); }; socket.onmessage = (event) => { const data = JSON.parse(event.data); if (data.type === "room_update") { setUsersInRoom(data.users.filter((user) => user !== myNameRef.current)); } }; }; 
Enter fullscreen mode Exit fullscreen mode

This function:

  • Validates the room code.
  • Generates a random user name if none exists.
  • Establishes a WebSocket connection.
  • Notifies the server that a user has joined.
  • Listens for updates about connected users.

2. File Selection & Compression

Users can select a file, which is then compressed before sending.

Compressing a File Using Pako.js

const sendFile = (file: File) => { const reader = new FileReader(); reader.onloadend = () => { const arrayBuffer = reader.result as ArrayBuffer; const uint8Array = new Uint8Array(arrayBuffer); const compressedData = pako.deflate(uint8Array); ws?.send(JSON.stringify({ type: "file", to: selectedConnection, fileName: file.name, fileData: Array.from(compressedData), from: myName, })); }; reader.readAsArrayBuffer(file); }; 
Enter fullscreen mode Exit fullscreen mode
  • Reads the file as an ArrayBuffer.
  • Converts it into a Uint8Array.
  • Compresses it using Pako’s deflate function.
  • Sends it over WebSocket.

3. Receiving & Decompressing Files

When a file is received, it is first decompressed before saving.

const handleIncomingFile = (data) => { const compressedData = new Uint8Array(data.fileData); try { const decompressedData = pako.inflate(compressedData); setIncomingFiles([...incomingFiles, { fileName: data.fileName, fileData: decompressedData }]); } catch (error) { console.error("Decompression failed:", error); } }; 
Enter fullscreen mode Exit fullscreen mode
  • Extracts the compressed file data.
  • Decompresses it using Pako’s inflate function.
  • Stores it for the user to download.

Backend Implementation

1. WebSocket Server Setup

import WebSocket, { WebSocketServer } from "ws"; const wss = new WebSocketServer({ port: 8080 }); const rooms: Record<string, { ws: WebSocket; name: string }[]> = {}; 
Enter fullscreen mode Exit fullscreen mode
  • Creates a WebSocket server.
  • Stores active rooms and connected users.

2. Handling User Connections

wss.on("connection", (ws) => { ws.on("message", (message) => { const data = JSON.parse(message.toString()); if (data.type === "join_room") { if (!rooms[data.roomCode]) rooms[data.roomCode] = []; rooms[data.roomCode].push({ ws, name: data.name }); broadcastRoom(data.roomCode); } }); }); 
Enter fullscreen mode Exit fullscreen mode
  • Adds users to a room.
  • Broadcasts updates when users join.

3. Handling File Transfers

function handleFileTransfer(data) { rooms[data.roomCode]?.forEach((client) => { if (client.name === data.to) { client.ws.send(JSON.stringify(data)); } }); } 
Enter fullscreen mode Exit fullscreen mode
  • Routes compressed file data to the intended recipient.

Conclusion & Future Enhancements

This WebSocket-based file-sharing system provides real-time, efficient file transfers with Pako compression to optimize bandwidth usage. Future improvements may include:

  • WebRTC for Peer-to-Peer transfers (eliminating the server bottleneck).
  • Multi-file transfer support.
  • End-to-end encryption for security.

This approach demonstrates how WebSockets, Pako.js, and file compression can be leveraged to build a high-performance file-sharing system.

Top comments (1)

Collapse
 
rohitnirban profile image
Rohit Yadav

I just tried IntraShare and I loved it! The real-time file transfer is incredibly smooth.
Can you provide open source if possible?