Skip to main content
The Manager class handles all file operations for the P2P system, including reading pieces from seeders and writing received pieces to disk for leechers.

Constructor

Creates a new file manager instance.
const manager = new Manager(
  './shared/file.txt',
  'r',
  65536
);
filePath
string
required
Path to the file to manage
mode
string
required
File opening mode:
  • 'r' - Read-only (for seeders with existing file)
  • 'w+' - Read/write (for leechers downloading file)
pieceSize
number
required
Size of each piece in bytes (typically 64 KiB = 65536 bytes)

Properties

filePath
string
Path to the file being managed
pieceSize
number
Size of each piece in bytes
fileHandle
FileHandle
Node.js FileHandle for low-level file operations
fileSize
number
Total size of the file in bytes
mode
string
File opening mode (‘r’ for read-only, ‘w+’ for read/write)

Methods

openFile()

Opens the file according to the specified mode.
await manager.openFile();
Behavior:
  • Read mode ('r'): Opens existing file and retrieves its size
  • Write mode ('w+'): Creates new file (or truncates if exists), size is initially 0
// From src/manager.js
this.fileHandle = await fsp.open(this.filePath, this.mode);
if (this.mode === 'r') {
  const stats = await this.fileHandle.stat();
  this.fileSize = stats.size;
} else {
  this.fileSize = 0;
}
Returns: Promise<void>

setSize(size)

Sets the file size (used for leechers when total size is known).
await manager.setSize(1048576); // Set to 1 MB
size
number
required
Total file size in bytes
Behavior:
  • Updates the fileSize property
  • Truncates or extends the file to the specified size
  • Fills with zeros if extending
this.fileSize = size;
await this.fileHandle.truncate(size);
Returns: Promise<void> Usage in Node class:
// When leecher receives file metadata
await this.fileManager.setSize(this.fileSize);

readPiece(index)

Reads a specific piece from the file.
const pieceData = await manager.readPiece(0); // Read first piece
console.log(`Read ${pieceData.length} bytes`);
index
number
required
Zero-based index of the piece to read
Behavior:
  • Calculates the file offset: index * pieceSize
  • Handles the last piece (may be smaller than pieceSize)
  • Reads data into a Buffer
const offset = index * this.pieceSize;
let length = this.pieceSize;
if (offset + length > this.fileSize) {
  length = this.fileSize - offset; // Last piece adjustment
}
const buffer = Buffer.alloc(length);
await this.fileHandle.read(buffer, 0, length, offset);
return buffer;
Returns: Promise<Buffer> - Buffer containing the piece data Usage in Node class:
// Seeder sending piece to peer
this.fileManager.readPiece(index).then(buffer => {
  const pieceMsg = {
    type: 'piece',
    index: index,
    data: buffer.toString('base64')
  };
  socket.write(JSON.stringify(pieceMsg) + '\n');
});

writePiece(index, dataBuffer)

Writes a piece to the file at the appropriate position.
const data = Buffer.from('Hello, World!');
await manager.writePiece(0, data); // Write to first piece
index
number
required
Zero-based index of the piece to write
dataBuffer
Buffer
required
Buffer containing the piece data to write
Behavior:
  • Calculates the file offset: index * pieceSize
  • Writes the buffer to the file at the calculated position
  • Does not validate piece size or integrity
const offset = index * this.pieceSize;
await this.fileHandle.write(dataBuffer, 0, dataBuffer.length, offset);
Returns: Promise<void> Usage in Node class:
// Leecher receiving piece from peer
const dataBuffer = Buffer.from(dataBase64, 'base64');
try {
  await this.fileManager.writePiece(index, dataBuffer);
} catch (err) {
  console.error(`Error writing piece ${index}:`, err);
}

computeHash()

Computes the SHA-1 hash of the entire file.
const hash = await manager.computeHash();
console.log(`File hash: ${hash}`);
// Output: File hash: da39a3ee5e6b4b0d3255bfef95601890afd80709
Behavior:
  • Creates a read stream from the file
  • Computes SHA-1 hash incrementally
  • Returns hexadecimal string representation
return new Promise((resolve, reject) => {
  const hash = crypto.createHash('sha1');
  const stream = fs.createReadStream(this.filePath);
  stream.on('data', chunk => {
    hash.update(chunk);
  });
  stream.on('end', () => {
    const result = hash.digest('hex');
    resolve(result);
  });
  stream.on('error', err => {
    reject(err);
  });
});
Returns: Promise<string> - SHA-1 hash in hexadecimal format (40 characters) Usage in Node class:
// Seeder computing file hash on initialization
try {
  this.fileHash = await this.fileManager.computeHash();
} catch (err) {
  console.error('Error calculating file hash:', err);
  process.exit(1);
}
// Leecher verifying downloaded file integrity
const downloadedHash = await this.fileManager.computeHash();
if (downloadedHash === this.fileHash) {
  console.log('Integrity verification: OK (hash matches).');
} else {
  console.warn('Warning: downloaded file hash differs from expected.');
}

close()

Closes the file and releases the file descriptor.
await manager.close();
Behavior:
  • Closes the file handle if open
  • Sets fileHandle to null
  • Should be called when file operations are complete
if (this.fileHandle) {
  await this.fileHandle.close();
  this.fileHandle = null;
}
Returns: Promise<void>

Piece Size Handling

The default piece size is 64 KiB (65,536 bytes):
this.pieceSize = 65536; // 64 KiB by default
For small files, the piece size is automatically adjusted:
if (this.fileSize < this.pieceSize) {
  this.pieceSize = this.fileSize;
  this.fileManager.pieceSize = this.fileSize;
}

Calculating Number of Pieces

this.numPieces = Math.ceil(this.fileSize / this.pieceSize);
Example:
  • File size: 200,000 bytes
  • Piece size: 65,536 bytes
  • Number of pieces: Math.ceil(200000 / 65536) = 4
    • Piece 0: 65,536 bytes
    • Piece 1: 65,536 bytes
    • Piece 2: 65,536 bytes
    • Piece 3: 3,392 bytes (last piece)

Error Handling

All methods return Promises and should be wrapped in try-catch blocks:
try {
  await manager.openFile();
  await manager.setSize(1048576);
  const piece = await manager.readPiece(0);
  await manager.writePiece(1, piece);
  const hash = await manager.computeHash();
  await manager.close();
} catch (err) {
  console.error('File operation error:', err);
}

Build docs developers (and LLMs) love