pyftpdlib
pyftpdlib is a very fast, scalable, and asynchronous Python FTP server library. It provides a high-level portable interface to easily write efficient FTP servers, being the most complete RFC-959 FTP server implementation available for Python. It supports FTPS (RFC-4217), IPv6 (RFC-2428), Unicode filenames (RFC-2640), and virtual users. The library is currently at version 2.2.0 and actively maintained.
Warnings
- breaking Python 2.7 support has been removed in pyftpdlib 2.0.0. Users requiring Python 2.7 compatibility must install version 1.5.10.
- gotcha As an asynchronous library, pyftpdlib will block the entire server if any long-running, blocking operations (e.g., `time.sleep()`, heavy database queries, slow disk I/O) are executed in the main event loop.
- gotcha When the FTP server is behind a Network Address Translator (NAT), clients may fail to establish passive data connections unless `FTPHandler.masquerade_address` and `handler.passive_ports` are explicitly configured.
- breaking Starting with version 2.0.0, the default SSL/TLS method for `TLS_FTPHandler` was changed from `SSLv23_METHOD` to `TLS_SERVER_METHOD`, disabling older and insecure SSLv2/SSLv3 protocols. This may break compatibility with very old FTP clients.
- gotcha On Python 3.14+, `MultiprocessFTPServer` may be broken on POSIX systems (excluding macOS) due to a change in the default `multiprocessing` method from 'fork' to 'forkserver'.
Install
-
pip install pyftpdlib
Imports
- DummyAuthorizer
from pyftpdlib.authorizers import DummyAuthorizer
- FTPHandler
from pyftpdlib.handlers import FTPHandler
- FTPServer
from pyftpdlib.servers import FTPServer
- TLS_FTPHandler
from pyftpdlib.handlers import TLS_FTPHandler
- ThreadedFTPServer
from pyftpdlib.servers import ThreadedFTPServer
Quickstart
import os
import logging
from pyftpdlib.authorizers import DummyAuthorizer
from pyftpdlib.handlers import FTPHandler
from pyftpdlib.servers import FTPServer
def main():
# Setup a dummy authorizer for managing virtual users
authorizer = DummyAuthorizer()
# Add a user with read/write permissions
ftp_user = os.environ.get('FTP_USER', 'user')
ftp_pass = os.environ.get('FTP_PASS', '12345')
home_dir = os.environ.get('FTP_HOME', os.getcwd())
authorizer.add_user(ftp_user, ftp_pass, home_dir, perm='elradfmwMT')
# Add an anonymous user with read-only permissions
authorizer.add_anonymous(home_dir)
# Instantiate FTP handler class
handler = FTPHandler
handler.authorizer = authorizer
# Define a customized banner
handler.banner = "pyftpdlib based FTP server ready."
# Specify a masquerade address and the range of ports for passive connections
# Uncomment and configure if behind a NAT
# handler.masquerade_address = '151.25.42.11'
# handler.passive_ports = range(60000, 65535)
# Instantiate FTP server class and listen on all interfaces, port 2121
address = ('', 2121)
server = FTPServer(address, handler)
# Set limits for connections
server.max_cons = 256
server.max_cons_per_ip = 5
# Configure logging
logging.basicConfig(level=logging.INFO, format='%(levelname)s:%(name)s:%(message)s')
# Start ftp server
print(f"Starting FTP server on {address[0] or '0.0.0.0'}:{address[1]} with user '{ftp_user}' and home '{home_dir}'")
server.serve_forever()
if __name__ == "__main__":
main()