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?