DropKey is a secure and temporary file-sharing platform built using the MERN stack with Cloudinary integration. It allows users to upload files, protect them with a password, share them using a short transfer code or QR code, limit the number of downloads, and automatically delete files after a fixed expiry time.
This project was developed as a skill-based full-stack portfolio project to demonstrate practical knowledge of:
- React & modern frontend development
- REST API design
- File uploads & streaming
- Cloudinary integration
- MongoDB data modeling
- Background cleanup using cron jobs
- Secure download handling
- ZIP generation and file management
- Frontend: https://dropkey.vercel.app
- Backend API: https://dropkey.onrender.com
The main aim of DropKey is to provide a simple, secure, and temporary file transfer system where files are automatically removed after use, reducing storage overhead and improving privacy.
- π Password-protected file sharing
- β³ Auto-expiry after 15 minutes
- π₯ Download limit control
- π¦ ZIP download for multiple files
- βοΈ Cloudinary storage for small files and videos
- πΎ Local storage for large files
- π§Ή Automatic cleanup of expired files
- π± QR code based sharing
- π Real-time upload progress
- π¨ Modern responsive UI with Tailwind CSS
- Upload up to 10 files at once
- Maximum 90 MB per file
- Maximum 500 MB total upload size
- Optional password protection
- Set download limit (1β4 downloads)
- Share using:
- Transfer code
- QR code
- Download files individually or as ZIP
- View file details before downloading
- Passwords are hashed using bcrypt
- Download limit enforcement
- Expiry-based access control
- Automatic deletion of expired transfers
- Cloudinary resource cleanup
- Local file cleanup
-
Real-time upload progress
-
Processing animation during upload
-
Responsive design
-
Error handling with toast notifications
-
QR scanner support
-
Background cleanup job using node-cron
| Technology | Purpose |
|---|---|
| React (Vite) | UI development |
| Tailwind CSS | Styling |
| Axios | API communication |
| React Icons | Icons |
| QR Scanner Library | QR code scanning |
| Sonner / Custom Toast | Notifications |
| Technology | Purpose |
|---|---|
| Node.js | Runtime |
| Express.js | API framework |
| Multer | File upload handling |
| Archiver | ZIP generation |
| bcrypt | Password hashing |
| nanoid | Transfer code generation |
| node-cron | Background cleanup |
| Technology | Purpose |
|---|---|
| MongoDB Atlas | Database |
| Cloudinary | Cloud storage for small files/videos |
| Local Server Storage | Large file storage |
| Service | Purpose |
|---|---|
| Vercel | Frontend hosting |
| Render | Backend hosting |
User
β
βΌ
React Frontend (Vercel)
β
βΌ
Express API (Render)
β
βββ MongoDB Atlas
βββ Cloudinary
βββ Local uploads folder
Browser
β
βΌ
Express + Multer
β
βββ Video or β€10MB βββΊ Cloudinary
β β
β βΌ
β Save URL + publicId
β
βββ >10MB ββββββββββββΊ Local Storage
β
βΌ
Save localPath
Transfer Code
β
βΌ
Validate code
β
βββ Expired? β
βββ Password valid? β
βββ Download limit reached? β
βΌ
Return file
β
βββ Cloudinary β Redirect
βββ Local β Stream download
βββ Multiple β Generate ZIP
DropKey/
βββ client/
β βββ src/
β β βββ api/
β β βββ components/
β β β βββ upload/
β β β βββ receive/
β β β βββ common/
β β βββ pages/
β β βββ main.jsx
β βββ package.json
β
βββ server/
β βββ src/
β β βββ config/
β β βββ controllers/
β β βββ middleware/
β β βββ models/
β β βββ routes/
β β βββ services/
β β βββ jobs/
β β βββ server.js
β βββ uploads/
β βββ package.json
β
βββ README.md
| Condition | Storage |
|---|---|
| Video files | Cloudinary |
| Files β€ 10MB | Cloudinary |
| Files > 10MB | Local server |
DropKey uses a hybrid storage approach to balance speed, reliability, and cloud usage limits.
-
Cloudinary is used for:
- Small files
- Images
- Videos
- Frequently shared lightweight content
-
Local storage is used for:
- Files larger than 10MB
- Large ZIP archives
- Temporary high-size transfers
This project is built on the Cloudinary free plan, which has limitations on:
- Storage quota
- Monthly bandwidth
- Transformation usage
- Large file handling
To avoid unnecessary cloud consumption and keep the project practical for a portfolio/demo environment, large files are intentionally stored on the server instead of Cloudinary.
This design helps:
- Reduce cloud storage usage
- Reduce bandwidth consumption
- Avoid hitting free-tier limits quickly
- Improve upload reliability for larger files
During development, it was observed that uploading every file to Cloudinary can introduce additional network latency, especially for larger files. Keeping large files on local temporary storage provides a faster and more stable experience for short-lived transfers.
- Multer saves the file temporarily.
- The file is uploaded to Cloudinary.
urlandpublicIdare stored in MongoDB.- The temporary local file is deleted.
- The file is stored in the server uploads folder.
localPathis stored in MongoDB.- The file is deleted:
- after the final allowed download, or
- when the transfer expires.
Small File / Video
β
Cloudinary
β
Download
β
Delete from Cloudinary
Large File
β
Local Server
β
Download
β
Delete from Local Storage
This approach keeps DropKey lightweight, cost-efficient, and suitable for temporary file sharing on a free-tier cloud setup.
- Password is optional.
- Passwords are hashed using bcrypt.
- Plain passwords are never stored.
- User can choose 1β4 downloads.
- Download count is incremented on successful download.
- After the final allowed download:
- Local files are deleted immediately.
- Cloudinary files are deleted after redirect/cleanup.
- MongoDB document is removed.
-
Each transfer expires after 15 minutes.
-
Expired transfers cannot be accessed.
-
A cron job runs every minute to remove expired resources.
DropKey provides two methods for receiving files:
- Transfer Code
- QR Code
Every upload generates a unique transfer code and a QR code.
Example:
Transfer Code: AB7XK2
Receive URL: https://dropkey.vercel.app/receive/AB7XK2
The QR code contains the receive URL and can be scanned directly from another device.
- User enters the transfer code.
- Frontend requests transfer details.
- Backend validates:
- Transfer exists
- Transfer is not expired
- Metadata is returned.
- User downloads the file.
- User opens scanner.
- QR code is scanned.
- Transfer code is extracted.
- Transfer details are loaded automatically.
- User downloads the file.
When only one file exists:
Backend redirects directly to the Cloudinary file URL.
Example:
https://res.cloudinary.com/.../file.mp4
Benefits:
- Faster download
- Reduced server load
- CDN delivery
- Better scalability
Backend serves the file using:
res.download()
The file is streamed directly from the server.
Benefits:
- Supports large files
- No Cloudinary bandwidth usage
- No external dependency
When more than one file exists:
- Files are collected.
- ZIP archive is generated dynamically.
- ZIP stream is sent to the user.
- No ZIP file is permanently stored on the server.
Technology Used:
- Archiver
Benefits:
- Saves disk space
- Faster cleanup
- No temporary ZIP storage required
Each transfer has a download limit.
Example:
Download Limit = 2
Download #1 β Allowed Download #2 β Allowed Download #3 β Blocked
Once the limit is reached:
- Transfer becomes unavailable
- Files are deleted
- Database record is removed
If a password exists:
- User enters password.
- Backend compares hash using bcrypt.
- Access granted only if password matches.
Passwords are never stored in plain text.
Only hashed versions are saved.
Base URL
http://localhost:5000/api/v1/files
Production:
https://dropkey.onrender.com/api/v1/files
POST /upload
Content-Type:
multipart/form-data
Body:
files[] password (optional) downloadLimit
{ "success": true, "data": { "transferCode": "AB7XK2", "totalFiles": 3, "totalSize": 15325212 } }
GET /:code
Example:
GET /AB7XK2
{ "success": true, "data": { "files": [...], "totalFiles": 3, "totalSize": 15325212, "downloadsLeft": 2, "isPasswordProtected": true, "expiresAt": "..." } }
POST /:code/download
{ "password": "123456" }
Single File: 302 Redirect (Cloudinary)
or
Binary File Stream
Multiple Files: ZIP Stream
400 - Password Required
401 - Incorrect Password
404 - Invalid Transfer Code
410 - Transfer Expired
500 - Internal Server Error
Upload API: Limited to prevent abuse.
Search API: Limited to prevent brute force attempts.
Download API: Limited to prevent excessive downloads.
Implemented using:
express-rate-limit
Create a .env file inside the server directory.
PORT=5000
MONGODB_URL=your_mongodb_connection_string
- CLOUDINARY_CLOUD_NAME=your_cloud_name
- CLOUDINARY_API_KEY=your_api_key
- CLOUDINARY_API_SECRET=your_api_secret
FRONTEND_URL=https://dropkey.vercel.app
- Never commit
.envfiles to GitHub. - Add
.envto.gitignore. - Set the same variables in Render Environment Variables.
git clone https://github.com/saurav-09/DropKey
cd DropKey
cd client
npm install
cd ../server
npm install
Create server/.env and add all required variables.
npm run dev
Server runs on:
cd ../client
npm run dev
Frontend runs on:
- Open frontend
- Select files
- Upload
- Copy transfer code
- Open receive page
- Download files
- Push project to GitHub
- Open Vercel
- Import repository
- Set root directory:
client - Deploy
VITE_API_URL=https://dropkey.onrender.com/api/v1
- Open Render
- Create Web Service
- Connect GitHub repository
- Set root directory:
server - Build command:
npm install
- Start command:
npm start
- PORT=5000
- MONGODB_URL=...
- CLOUDINARY_CLOUD_NAME=...
- CLOUDINARY_API_KEY=...
- CLOUDINARY_API_SECRET=...
- FRONTEND_URL=https://dropkey.vercel.app
The backend allows:
- Main Vercel domain
- Vercel preview deployments
Render provides temporary filesystem storage.
This is acceptable for DropKey because:
- Files expire after 15 minutes
- Expired files are cleaned automatically
- The project is intended for temporary sharing
Files uploaded to Cloudinary remain available until:
- Download limit is reached, or
- Cron cleanup deletes them.
- Implemented using bcrypt
- Salt rounds: 10
Prevents excessive file uploads.
Prevents transfer code enumeration.
Prevents abuse and repeated requests.
Every request checks:
- Transfer exists
- Transfer is not expired
Checks are performed in this order:
- Transfer exists
- Transfer not expired
- Password valid
- Download limit available
Expired resources are removed from:
- MongoDB
- Cloudinary
- Local server storage
Transfer codes are short-lived and automatically invalidated.
Built DropKey, a secure temporary file-sharing platform using React, Node.js, Express, MongoDB, and Cloudinary with password protection, download limits, QR-based sharing, ZIP downloads, and automatic file cleanup using cron jobs.
Developed a full-stack temporary file transfer application that supports secure sharing through transfer codes and QR codes. Implemented hybrid storage using Cloudinary for small files and local storage for larger files, added bcrypt-based password protection, download-limit enforcement, dynamic ZIP generation, and automatic deletion of expired files from MongoDB, Cloudinary, and server storage.
Saurav
- MERN Stack Developer
- React β’ Node.js β’ Express β’ MongoDB β’ Cloudinary
- DSA in Java
GitHub: https://github.com/saurav-09
LinkedIn: www.linkedin.com/in/saurav-dev
This project is licensed under the MIT License.
You are free to:
- Use
- Modify
- Distribute
- Learn from the code
For educational and portfolio purposes.
- React
- Express.js
- MongoDB Atlas
- Cloudinary
- Tailwind CSS
- Render
- Vercel
DropKey was created as a real-world full-stack practice project focused on secure temporary file sharing, cloud integration, streaming downloads, background cleanup, and modern user experience design.
If you found this project useful, consider giving it a β on GitHub.