building a peer file sharing multi client app: java nginx docker aws....
๐ PeerLink: A Peer-to-Peer File Sharing App in Java and Next.js
PeerLink is a lightweight, full-stack peer-to-peer (P2P) file sharing application that allows users to upload and share files directly between two machines using an invite code mechanism. Built with Java on the backend and Next.js on the frontend, this project dives deep into systems-level programming topics like Java Streams, multi-threading, socket communication, and HTTP protocol parsing.
๐ GitHub repo: [https://github.com/singhdevhub-lovepreet/PeerLink]
๐ง Motivation Behind PeerLink
As a backend and systems enthusiast, I wanted a project that would allow me to explore three things in depth:
1. Java Streams
Building a file transfer mechanism gave me the perfect opportunity to use:
- Input/Output streams (
InputStream
,OutputStream
) - Buffered streams for large file support
- Byte stream manipulation
2. Java Threading + Socket Server
Peer-to-peer means real-time parallel interactions. This required:
- Spawning new threads per download session
- Concurrent socket listening/serving
- Thread-safe data handling for file sharing
3. HTTP Protocol Parsing (Minimalist HTTP Server)
Instead of relying on full-fledged frameworks, I implemented:
- Barebones HTTP header parsing from socket input
- Manually serving HTTP responses with correct headers
- Handling multipart uploads/downloads via raw Java streams
PeerLink emerged as a side project turned educational sandbox for these ideas.
๐งฑ Project Architecture
Tech Stack
Layer | Technology |
---|---|
Frontend | Next.js (React 18, App Router) |
Backend | Java 11 (Maven project) |
Protocol | HTTP over custom sockets |
Data Flow | File Streams, P2P port transfer |
Runtime | Local server on port 8080 (backend) and 3000 (frontend) |
Project Structure
PeerLink/
โโโ src/main/java/p2p
โ โโโ App.java # Main server entry
โ โโโ controller/ # REST endpoints
โ โโโ service/ # File serving logic
โ โโโ utils/ # Helper classes (ports, streams)
โโโ ui/ # Next.js frontend
โ โโโ src/app, components/ # UI pages and components
โโโ start.sh / start.bat # Quickstart scripts
โโโ README.md
๐ ๏ธ How It Works
File Upload
- The user drags and drops a file in the UI.
- The file is POSTed to the backend.
- The backend stores it and spawns a dedicated file server on a random port.
- This port acts as the "invite code".
File Sharing (Invite Code)
- The user shares the invite code (port number) with their peer.
- No complex tokens or auth โ simple and fast.
File Download
- The peer enters the invite code.
- The frontend initiates a fetch request to that port.
- The backend file server serves the file over HTTP directly.
๐งต Concurrency with Threaded File Servers
Each file transfer happens in its own thread:
new Thread(() -> {
try (ServerSocket serverSocket = new ServerSocket(port)) {
Socket client = serverSocket.accept();
// Serve the file via OutputStream
} catch (IOException e) {
// handle errors
}
}).start();
This allows:
- Multiple simultaneous transfers
- No main-thread blocking
- Independent file servers per port
Thread Safety
- The file server uses synchronized blocks and error handling
- No shared mutable state across transfers
Architecture
๐ Minimal HTTP Server (Custom Parser)
Instead of using Spring or Jetty, I manually parsed HTTP headers:
BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String line;
while (!(line = reader.readLine()).isEmpty()) {
// Parse headers manually
}
This low-level approach gave me:
- Full control over the HTTP response
- Better understanding of how clients and servers communicate
- Ability to optimize for a single use case (file download)
Example response:
HTTP/1.1 200 OK
Content-Type: application/octet-stream
Content-Disposition: attachment; filename="myfile.txt"
Content-Length: 12345
Then the raw file bytes are streamed via OutputStream
.
๐ฅ๏ธ Frontend: Next.js UI
The frontend is built using Next.js App Router with two core components:
- FileUploader: drag-and-drop zone, previews, upload button
- FileDownloader: input field for invite code, progress bar
It uses fetch()
to communicate with the backend and supports file preview + download with progress.
๐ Security Notes
- โ ๏ธ This is a demo/prototype.
- No encryption or authentication is implemented.
- It runs only on local/private networks (recommended).
- Before production:
- Add HTTPS and TLS
- Authenticate invite codes
- Set rate-limits and connection limits
๐ฆ Deployment
You can run PeerLink in several ways:
Localhost:
./start.sh
Docker:
docker-compose up
VPS or Cloud (e.g., Railway, Heroku):
- Expose backend ports via secure tunnel (e.g., ngrok)
- Frontend can be deployed to Vercel/Netlify
๐งช Future Improvements
- โ Add authentication per invite code
- โ Serve files via HTTPS using embedded TLS server
- โ Peer discovery and NAT traversal
- โ Use WebRTC for direct file transfer
- โ Replace invite code with QR code scanner
๐ Final Thoughts
PeerLink taught me how to:
- Use Java Streams to efficiently handle large files
- Run concurrent socket servers using threading
- Build a mini HTTP server and understand protocol internals
- Integrate backend logic with a modern frontend (Next.js)
If you're a backend developer wanting to learn systems-level Java โ this project is a perfect playground. Peer-to-peer doesn't have to be complex. Sometimes, all you need is a good port and a clean stream.
๐ Try It Out
- Clone the repo
- Run
./start.sh
- Drop a file โก Share the code โก Receive the file
Let the peer-to-peer fun begin ๐