A multithreaded client-server file storage system written in Java, built as part of the JetBrains Hyperskill Java Backend Developer track.
The File Server allows clients to store, retrieve, and delete files on a remote server over a TCP socket connection. Files are persisted on disk and referenced by either their filename or a server-assigned integer ID. The server handles multiple simultaneous client connections using a thread pool.
- TCP socket communication between client and server
- Concurrent client handling via
ExecutorServicethread pool - ID-based file access — server assigns a unique integer ID on
PUT, which the client can use forGETandDELETE - Persistence across restarts — the ID-to-filename map is serialized to disk using
ObjectOutputStream/ObjectInputStream - Binary file transfer support
- Graceful shutdown — server exits cleanly on client request
File-Server/
├── src/
│ └── server/
│ ├── Main.java # Server entry point
│ ├── Server.java # Accepts connections, manages thread pool
│ ├── RequestHandler.java # Handles individual client sessions
│ └── Storage.java # File I/O and ID map management
│ └── client/
│ ├── Main.java # Client entry point
│ └── Client.java # Sends requests to the server
└── README.md
The server listens on a configured port and accepts incoming client connections. Each connection is handed off to a RequestHandler running in a thread pool managed by ExecutorService.
ExecutorService pool = Executors.newFixedThreadPool(N_THREADS);
pool.submit(() -> handleClient(socket));The server maintains a HashMap<Integer, String> idMap that maps integer file IDs to their filenames on disk. This map is serialized to a file after every write operation so it survives server restarts.
Clients send plain-text commands over the socket:
| Command | Format | Description |
|---|---|---|
| PUT | PUT <filename> <data> |
Store a file; server returns the assigned ID |
| GET | GET <id|filename> |
Retrieve a file by ID or name |
| DELETE | DELETE <id|filename> |
Delete a file by ID or name |
| EXIT | EXIT |
Shut down the server |
On every PUT, the server updates idMap and serializes the entire map to disk:
// Save
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(ID_MAP_PATH));
oos.writeObject(idMap);
// Load on startup
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(ID_MAP_PATH));
idMap = (HashMap<Integer, String>) ois.readObject();- Java 11+
- Any IDE (IntelliJ IDEA recommended) or
javac/javafrom the command line
cd src
javac server/*.java
java server.Maincd src
javac client/*.java
java client.Main- Java Sockets (
ServerSocket,Socket) - Multithreading with
ExecutorServiceand thread pools - Java Serialization (
ObjectOutputStream/ObjectInputStream) - Binary file I/O with
DataInputStream/DataOutputStream - Synchronized access to shared state
| Stage | What was added |
|---|---|
| 1 | Basic single-client GET/PUT over sockets |
| 2 | File storage to disk |
| 3 | DELETE support; named file access |
| 4 | Multithreading, ID-based access, serialized persistence |