Modern Extensible FTP Server

4.6.3 · active · verified Sun Apr 19

ftp-srv is a modern and extensible FTP server library for Node.js, currently stable at version 4.6.3. It provides a promise-based API for creating FTP and FTPS (Explicit & Implicit TLS) servers with support for both passive and active data transfers. The project maintains an active release cadence, frequently publishing bug fixes and minor features, as seen in recent updates like recursive directory creation and TypeScript declaration improvements. Its key differentiators include a highly extensible file system per connection, allowing for custom storage backends, and comprehensive support for various FTP commands and connection modes. It differentiates itself by offering detailed control over passive connection URLs and port ranges, addressing complexities often found in network deployments behind NATs or firewalls. It requires Node.js version 12 or higher.

Common errors

Warnings

Install

Imports

Quickstart

Demonstrates how to initialize an FTP server, handle anonymous logins, and specify a root directory. It includes basic error handling and prepares a dummy `ftp_root` directory in the current working directory, making it fully runnable out of the box.

import { FtpSrv, errors } from 'ftp-srv';
import path from 'path';
import { promises as fs } from 'fs'; // For creating the dummy ftp_root directory

const port = 21;
const ftpServer = new FtpSrv({
    url: `ftp://0.0.0.0:${port}`,
    anonymous: true
});

ftpServer.on('login', ({ connection, username, password }, resolve, reject) => {
    // In a real application, you would validate credentials against a database
    // For this example, 'anonymous' allows access to a specific root path
    if (username === 'anonymous' && password === 'anonymous') {
        const userRoot = path.join(process.cwd(), 'ftp_root');
        // A real scenario might dynamically create/select a user's directory
        return resolve({ root: userRoot });
    }
    return reject(new errors.GeneralError('Invalid username or password', 401));
});

ftpServer.listen().then(() => {
    console.log(`FTP server started on ftp://0.0.0.0:${port}`);
    console.log('Connect with anonymous:anonymous to access', path.join(process.cwd(), 'ftp_root'));
}).catch((err: Error) => {
    console.error('Failed to start FTP server:', err);
    process.exit(1);
});

// Helper to ensure the FTP root directory exists and contains a sample file
async function ensureFtpRootDirectory() {
    const rootPath = path.join(process.cwd(), 'ftp_root');
    try {
        await fs.mkdir(rootPath, { recursive: true });
        const welcomeFilePath = path.join(rootPath, 'welcome.txt');
        await fs.writeFile(welcomeFilePath, 'Welcome to the ftp-srv server!\nThis is a sample file.', { flag: 'wx' }); // 'wx' to only write if file doesn't exist
        console.log(`Ensured FTP root directory at ${rootPath}`);
    } catch (error: any) {
        if (error.code !== 'EEXIST') { // Ignore if directory/file already exists
            console.error('Error setting up FTP root directory:', error);
        }
    }
}
ensureFtpRootDirectory();

view raw JSON →