npm install simple-ftpdsimple-ftpd is a event based ftp-server for node.js.
- Supports multiple modern ftp clients such as FileZilla, WinSCP, Transmission.
- A real File System is not required.
- For now it only supports basic authentication.
- It supports passive mode only.
- It only supports streams for writing and reading files.
- It currently only supports a subset of FTP commands, but it should be enough for any modern FTP client.
- It is intended to be used mainly to create local network ftp servers.
Used pyftpdlib extensively as a reference for the FTP protocol specification, as reading RFC documents is super boring :=)
``sh`
yarn add simple-ftpdor
npm install simple-ftpd --save
`js`
const ftpd = require('ftpd')
Syntax: ftpd(opts, sessionCallback) or ftpd(host:port, sessionCallback)
Should you need more control, the ftpd function will return a net.Server instance.
Simple, fs-based ftp server implementation example:
`js
'use strict'
const ftpd = require('ftpd')
ftpd({ host: '127.0.0.1', port: 1337, root: '/public/files' }, (session) => {
session.on('pass', (username, password, cb) => {
if (username === 'superadmin' && password === '53cr3t') {
session.readOnly = false
session.root = '/private/secret/files'
cb(null, 'Welcome admin')
} else {
cb(null, 'Welcome guest')
}
})
session.on('stat', fs.stat)
// AKA
// session.on('stat', (pathName, cb) => {
// fs.stat(pathName, cb)
// })
session.on('readdir', fs.readdir)
// AKA
// session.on('readdir', (pathName, cb) => {
// fs.readdir(pathName, cb)
// })
session.on('read', (pathName, offset, cb) => {
cb(null, fs.createReadStream(pathName, { start: offset }))
})
session.on('write', (pathName, offset, cb) => {
cb(null, fs.createWriteStream(pathName, { start: offset }))
})
// I'd do some checking if I were you, but hey.
session.on('mkdir', fs.mkdir)
session.on('unlink', fs.unlink)
session.on('rename', fs.rename)
session.on('remove', require('rimraf'))
})
`
| option | | description |
| --- | --- | --- |
| host | required | Must be a valid ipv4 address (for now). Defaults to 127.0.0.1 |port
| | required | Server port. Defaults to 1337. |readOnly
| | optional | Disables client write requests. Defaults to true. Can be overridden on a connection-basis by setting the readOnly property on the session object before logging in. |root
| | optional | The path passed to events will always be joined to root. Can be overridden on a connection-basis by setting the root property on the session object before logging in. The ftp client will not see root, and will think he's at /. This is simply a convenience to avoid manually joining the path you get in every event. |maxConnections
| | optional | Maximum number of server connections. Defaults to 10. |
Passive connections will be initialized on an unused port assigned by the os.
Instead of options, you can pass ${host}:${port} ("192.168.1.10:1337").
Every event gets some arguments and a node-style callback you must call with an Error object (if any) and results.
If you do not listen to a specific event, then that feature will become unavailable to the client.
For example, if you do not listen to write events, the client will get a 502 error when trying to write files.
The client wants to login with username and password.cb()
Will consider the user logged in if no error is passed to
User names are always accepted, because (from pyftpdlib):
> In order to prevent a
> malicious client from determining valid usernames on a server,
> it is suggested by RFC-2577 that a server always return 331 to
> the USER command and then reject the combination of username
> and password for an invalid username when PASS is provided later.
The client wants to read the file at pathName, starting at offset.cb(null, readStream)
Requires a readable stream to be passed as .
The client wants to write a file at pathName, starting at offset.cb(null, writeStream)
Requires a writable stream to be passed as .
the client wants the stats to a file or directory at pathName.fs.stat
Requires a -like object to be passed as cb(null, stat).
Properties are:
`js`
{
// fs.stat mode
mode: 16822,
// size in bytes
size: 12345,
// lastModifiedTime as Date or number (or whatever gets parsed by moment)
mtime: Date.now(),
// optional, will use it for display if available, otherwise "owner"
uname: 'kamicane',
// optional, will use it for display if available, otherwise "group"
gname: 'admins',
}
The client wants a list of files in the directory pathName. requires an array of file names, relative to pathName, to be passed as cb(null, list).
The client wants to create a directory at pathName.cb()
Will consider the directory written if no error is passed to
The client wants to delete the file at pathName.cb()
Will consider the file deleted if no error is passed to
The client wants to remove a directory, and all of its contents, at pathName.cb()
Will consider the directory and its contents removed if no error is passed to
The client wants to rename a file or directory, from fromName to toName.cb()
Will consider the file renamed if no error is passed to
When installed globally, ftpd will provide a small cli, which will quickly create a fs-based ftp server.
You can use it like this:
`sh`
ftp-server /public/files --host 192.168.0.1 --port 1234 --max-connections 10
* --host will default to 127.0.0.1--port
* will default to 1337--max-connections
* will default to 10--read-only
* will default to trueprocess.cwd()
* Unless specified, the ftp root will default to
The cli will accept any login, so be careful when setting --read-only to false`.
MIT