Python’s ftplib lets you blast files around via FTP, but it’s not just a dumb pipe.
from ftplib import FTP
ftp = FTP('ftp.example.com')
ftp.login('user', 'password')
# Uploading a file
with open('local_file.txt', 'rb') as fp:
ftp.storbinary('STOR remote_file.txt', fp)
# Downloading a file
with open('downloaded_file.txt', 'wb') as fp:
ftp.retrbinary('RETR remote_file.txt', fp.write)
ftp.quit()
This code is the skeleton. The STOR command tells the FTP server to store the incoming data stream as a file named remote_file.txt. RETR is the opposite: retrieve the file named remote_file.txt and feed its data stream into the provided callback function (fp.write in this case, which writes chunks of data to the local file). The 'rb' and 'wb' modes are crucial here; FTP, especially for binary files, expects raw bytes, not decoded text.
The Problem It Solves: Remote File Management
Imagine you have a web server, a data logger, or any system that generates files you need to access or update from another machine. FTP is a common, albeit older, protocol for this. ftplib bridges the gap, letting your Python scripts act as an FTP client to interact with these remote resources. You can automate backups, distribute configuration files, or pull data for processing without manual intervention.
How It Works Internally: Control and Data Connections
When you connect to an FTP server, ftplib first establishes a control connection on port 21. This is where commands like USER, PASS, STOR, and RETR are sent, and where the server sends its responses (like "200 OK" or "550 File not found").
For actual file transfers, FTP opens a separate data connection. The mode of this connection (active or passive) dictates how this data connection is established.
- Active Mode: The client tells the server its IP address and a port number. The server then initiates a connection back to the client on that specified port to send or receive data. This can be problematic with firewalls on the client side.
- Passive Mode: The client sends a
PASVcommand. The server responds with an IP address and port number that the client should connect to for the data transfer. This is generally preferred as it avoids firewall issues on the client.ftplibdefaults to passive mode, which is why it usually "just works."
When you call ftp.storbinary or ftp.retrbinary, ftplib handles the negotiation of the data connection (usually passive), sending the necessary commands (PASV, PORT if active, STOR/RETR), and then streaming the data over that connection.
The Levers You Control
FTP('hostname', user, passwd): The constructor. You can pass credentials here directly, orlogin()separately.ftp.login(user, passwd, account=''): Authenticates with the server. Theaccountparameter is rarely used.ftp.cwd(pathname): Change directory on the remote server.ftp.nlst(): Get a list of filenames in the current directory.ftp.dir(): Get a more detailed directory listing (likels -l).ftp.size(filename): Get the size of a remote file.ftp.storbinary(command, file, blocksize=8192, callback=None, rest=None): Uploads a file.commandis usuallySTOR filename.fileis a file-like object opened in binary read mode ('rb').blocksizecontrols how much data is read fromfileand sent at once.callbackcan be a function to show progress.restis for resuming interrupted transfers.ftp.retrbinary(command, callback, blocksize=8192, rest=None): Downloads a file.commandis usuallyRETR filename.callbackis a function that receives data chunks (e.g.,file.write).ftp.mkd(pathname): Make a new directory on the remote server.ftp.rmd(pathname): Remove a directory on the remote server.ftp.delete(filename): Delete a file on the remote server.ftp.pwd(): Get the current working directory on the remote server.ftp.quit(): Closes the control connection.
The One Thing Most People Don’t Know
The rest parameter in storbinary and retrbinary is incredibly powerful for resuming interrupted transfers. If a download or upload fails partway through, you don’t have to start from scratch. You can get the size of the partially transferred file on the remote server and then pass that size (in bytes) as the rest argument to the STOR or RETR command. ftplib will then tell the server to start sending/receiving from that byte offset, and you can continue writing/reading to/from your local file from where you left off.
The next step is often dealing with FTP servers that require explicit TLS/SSL encryption using ftplib.FTP_TLS.