FTP (File Transfer Protocol) is one of the oldest and most widely used protocols for transferring files between a client and a remote server. Whether you need to automate backups, sync files across systems, or integrate with legacy infrastructure, knowing how to interact with an FTP server programmatically is a valuable skill.
Python's built-in ftplib module makes it straightforward to connect to an FTP server, upload files, download files, and manage remote directories, all without installing any third-party packages.
This guide walks you through the entire process with complete, runnable examples.
What Is FTP?
File Transfer Protocol (FTP) is an application-layer protocol that runs on top of TCP. It is designed specifically for transferring files between a client and a server over a network. FTP uses two separate connections:
- Control connection (port 21): handles commands and responses (login, directory listing, etc.).
- Data connection (port 20): transfers the actual file data.
While FTP is widely supported, it transmits data (including credentials) in plain text. For secure transfers, consider using FTPS (FTP over TLS) via ftplib.FTP_TLS, which is also covered in this guide.
What Is the ftplib Module?
The ftplib module is part of Python's standard library, so no installation required. It provides the FTP class (and FTP_TLS for secure connections) that implements the client side of the FTP protocol. With it, you can:
- Connect and authenticate with an FTP server
- Upload and download files
- List, create, rename, and delete remote directories and files
- Switch between binary and text transfer modes
Connecting to an FTP Server
The first step in any FTP operation is establishing a connection and authenticating.
import ftplib
HOSTNAME = "ftp.example.com"
USERNAME = "user@example.com"
PASSWORD = "password"
ftp_server = ftplib.FTP(HOSTNAME, USERNAME, PASSWORD)
ftp_server.encoding = "utf-8"
print(ftp_server.getwelcome())Sample output:
220 Welcome to the Test FTP ServerUsing Environment Variables for Credentials
import os
import ftplib
HOSTNAME = os.getenv("FTP_HOST", "ftp.example.com")
USERNAME = os.getenv("FTP_USER", "user@example.com")
PASSWORD = os.getenv("FTP_PASS", "password")
ftp_server = ftplib.FTP(HOSTNAME, USERNAME, PASSWORD)
ftp_server.encoding = "utf-8"Uploading a File to an FTP Server
To upload a file, use the storbinary() method, which transfers data in binary mode. The command passed to the method follows the format STOR <filename>.
import ftplib
HOSTNAME = "ftp.example.com"
USERNAME = "user@example.com"
PASSWORD = "password"
ftp_server = ftplib.FTP(HOSTNAME, USERNAME, PASSWORD)
ftp_server.encoding = "utf-8"
filename = "example.txt"
with open(filename, "rb") as file:
ftp_server.storbinary(f"STOR {filename}", file)
print(f"'{filename}' uploaded successfully.")
print("\nRemote directory contents:")
ftp_server.dir()
ftp_server.quit()Sample output:
'example.txt' uploaded successfully.
Remote directory contents:
-rw-r--r-- 1 0 0 25 Jul 23 12:00 example.txtHow It Works
- The file is opened in binary read mode (
"rb"). storbinary(command, file)reads the file object and sends its contents to the server.- The
STORcommand tells the FTP server to store the incoming data as a file with the given name. ftp_server.dir()lists the remote directory contents to verify the upload.
Downloading a File from an FTP Server
To download a file, use the retrbinary() method with the RETR <filename> command. The method accepts a callback function that processes each chunk of data, typically file.write.
import ftplib
HOSTNAME = "ftp.example.com"
USERNAME = "user@example.com"
PASSWORD = "password"
ftp_server = ftplib.FTP(HOSTNAME, USERNAME, PASSWORD)
ftp_server.encoding = "utf-8"
filename = "example.txt"
with open(filename, "wb") as file:
ftp_server.retrbinary(f"RETR {filename}", file.write)
print(f"'{filename}' downloaded successfully.")
with open(filename, "r") as file:
print(f"\nFile content:\n{file.read()}")
ftp_server.quit()Sample output:
'example.txt' downloaded successfully.
File content:
Hello from Tutorial Reference!How It Works
- A local file is opened in binary write mode (
"wb"). retrbinary(command, callback)retrieves the remote file in chunks and passes each chunk to the callback (file.write).- After the download completes, the file is read back in text mode to verify its contents.
Listing Remote Files and Directories
Before uploading or downloading, you may want to inspect the remote directory structure:
import ftplib
ftp_server = ftplib.FTP("ftp.example.com", "user@example.com", "password")
ftp_server.encoding = "utf-8"
print("Detailed listing:")
ftp_server.dir()
print()
print("File names only:")
files = ftp_server.nlst()
for f in files:
print(f" {f}")
ftp_server.quit()| Method | Description |
|---|---|
ftp.dir() |
Prints a detailed directory listing (like ls -l) |
ftp.nlst() |
Returns a list of file and directory names |
ftp.mlsd() |
Returns an iterable of (name, facts) tuples with metadata |
Navigating Directories on the Server
You can change the working directory, create new directories, and remove them:
import ftplib
ftp_server = ftplib.FTP("ftp.example.com", "user@example.com", "password")
ftp_server.encoding = "utf-8"
print(f"Current directory: {ftp_server.pwd()}")
ftp_server.mkd("test_folder")
ftp_server.cwd("test_folder")
print(f"Changed to: {ftp_server.pwd()}")
ftp_server.cwd("..")
ftp_server.rmd("test_folder")
ftp_server.quit()Adding Error Handling
Production code should handle connection failures, authentication errors, and missing files gracefully:
import ftplib
HOSTNAME = "ftp.example.com"
USERNAME = "user@example.com"
PASSWORD = "password"
try:
ftp_server = ftplib.FTP(HOSTNAME, USERNAME, PASSWORD, timeout=10)
ftp_server.encoding = "utf-8"
print("Connected successfully.")
filename = "example.txt"
with open(filename, "wb") as file:
ftp_server.retrbinary(f"RETR {filename}", file.write)
print(f"'{filename}' downloaded successfully.")
except ftplib.error_perm as e:
print(f"FTP permission error: {e}")
except ftplib.all_errors as e:
print(f"FTP error: {e}")
except FileNotFoundError:
print(f"Local file path is invalid.")
finally:
try:
ftp_server.quit()
print("Connection closed.")
except Exception:
passCommon Mistake: Forgetting to Close the Connection
Leaving an FTP connection open wastes server resources and may cause your program to hang:
ftp_server = ftplib.FTP(HOSTNAME, USERNAME, PASSWORD)
ftp_server.retrbinary(f"RETR {filename}", open(filename, "wb").write)The correct approach is to always close the connection, ideally using a try/finally block or a context manager:
ftp_server = ftplib.FTP(HOSTNAME, USERNAME, PASSWORD)
try:
with open(filename, "wb") as file:
ftp_server.retrbinary(f"RETR {filename}", file.write)
finally:
ftp_server.quit()Using FTPS for Secure Transfers
Standard FTP sends credentials and data in plain text, which is a security risk. For encrypted transfers, use ftplib.FTP_TLS:
import ftplib
HOSTNAME = "ftp.example.com"
USERNAME = "user"
PASSWORD = "password"
ftp_server = ftplib.FTP_TLS(HOSTNAME, USERNAME, PASSWORD)
ftp_server.prot_p()
ftp_server.encoding = "utf-8"
ftp_server.dir()
ftp_server.quit()Complete Example: Upload and Download
Here is a complete, self-contained script that demonstrates both uploading and downloading a file:
import ftplib
import os
HOSTNAME = "ftp.example.com"
USERNAME = "user@example.com"
PASSWORD = "password"
FILENAME = "test_upload.txt"
def create_sample_file(filename):
"""Create a sample text file for testing."""
with open(filename, "w") as f:
f.write("Hello! This file was uploaded via Python ftplib.\n")
print(f"Created local file: {filename}")
def upload_file(ftp, filename):
"""Upload a file to the FTP server."""
with open(filename, "rb") as file:
ftp.storbinary(f"STOR {filename}", file)
print(f"Uploaded: {filename}")
def download_file(ftp, remote_filename, local_filename):
"""Download a file from the FTP server."""
with open(local_filename, "wb") as file:
ftp.retrbinary(f"RETR {remote_filename}", file.write)
print(f"Downloaded: {remote_filename} -> {local_filename}")
def main():
create_sample_file(FILENAME)
try:
ftp_server = ftplib.FTP(HOSTNAME, USERNAME, PASSWORD, timeout=15)
ftp_server.encoding = "utf-8"
print(f"Connected to {HOSTNAME}")
upload_file(ftp_server, FILENAME)
print("\nRemote directory:")
ftp_server.dir()
downloaded_name = "downloaded_copy.txt"
download_file(ftp_server, FILENAME, downloaded_name)
with open(downloaded_name, "r") as f:
print(f"\nDownloaded file content:\n{f.read()}")
except ftplib.all_errors as e:
print(f"FTP error: {e}")
finally:
ftp_server.quit()
print("Connection closed.")
for f in [FILENAME, "downloaded_copy.txt"]:
if os.path.exists(f):
os.remove(f)
if __name__ == "__main__":
main()Quick Reference: Key ftplib Methods
| Method | Description |
|---|---|
FTP(host, user, passwd) |
Connect and authenticate |
storbinary(cmd, file) |
Upload a file in binary mode |
retrbinary(cmd, callback) |
Download a file in binary mode |
dir() |
Print detailed directory listing |
nlst() |
Return list of file names |
cwd(path) |
Change working directory |
pwd() |
Print current working directory |
mkd(dirname) |
Create a directory |
rmd(dirname) |
Remove a directory |
delete(filename) |
Delete a file |
rename(old, new) |
Rename a file or directory |
quit() |
Close the connection gracefully |
Conclusion
Python's built-in ftplib module provides everything you need to interact with FTP servers, from simple file uploads and downloads to directory management and secure FTPS connections:
- Use
storbinary()to upload files andretrbinary()to download them. - Always close connections with
quit()in afinallyblock. - Add error handling with
ftplib.all_errorsandftplib.error_permfor robust production code. - Use
FTP_TLSinstead of plainFTPwhen security matters. - Store credentials in environment variables, not in your source code.
With these techniques, you can automate file transfers, build backup systems, or integrate with any FTP-based infrastructure reliably and securely.