Python's ftplib module provides an FTP client for uploading, downloading, and managing files on FTP servers.

Basic Connection

from ftplib import FTP
 
# Connect and login
ftp = FTP('ftp.example.com')
ftp.login('username', 'password')
 
# Show current directory
print(ftp.pwd())
 
# List files
ftp.dir()
 
# Disconnect
ftp.quit()

Secure Connection (FTPS)

from ftplib import FTP_TLS
 
# Implicit TLS
ftp = FTP_TLS('ftp.example.com')
ftp.login('username', 'password')
ftp.prot_p()  # Switch to secure data connection
 
# Do work...
 
ftp.quit()

Context Manager

from ftplib import FTP
 
with FTP('ftp.example.com') as ftp:
    ftp.login('username', 'password')
    ftp.dir()
# Automatically closes connection

Downloading Files

from ftplib import FTP
 
with FTP('ftp.example.com') as ftp:
    ftp.login('username', 'password')
    
    # Download binary file
    with open('local_file.zip', 'wb') as f:
        ftp.retrbinary('RETR remote_file.zip', f.write)
    
    # Download text file
    lines = []
    ftp.retrlines('RETR readme.txt', lines.append)
    content = '\n'.join(lines)

Uploading Files

from ftplib import FTP
 
with FTP('ftp.example.com') as ftp:
    ftp.login('username', 'password')
    
    # Upload binary file
    with open('local_file.zip', 'rb') as f:
        ftp.storbinary('STOR remote_file.zip', f)
    
    # Upload text file
    with open('readme.txt', 'rb') as f:
        ftp.storlines('STOR readme.txt', f)

Directory Operations

from ftplib import FTP
 
with FTP('ftp.example.com') as ftp:
    ftp.login('username', 'password')
    
    # Current directory
    print(ftp.pwd())  # /home/user
    
    # Change directory
    ftp.cwd('/public')
    
    # Go up one level
    ftp.cwd('..')
    
    # Create directory
    ftp.mkd('new_folder')
    
    # Remove directory
    ftp.rmd('empty_folder')

File Operations

from ftplib import FTP
 
with FTP('ftp.example.com') as ftp:
    ftp.login('username', 'password')
    
    # Delete file
    ftp.delete('old_file.txt')
    
    # Rename file
    ftp.rename('old_name.txt', 'new_name.txt')
    
    # Get file size
    size = ftp.size('file.zip')
    print(f"Size: {size} bytes")
    
    # Get modification time
    mtime = ftp.sendcmd('MDTM file.txt')
    print(mtime)

Listing Files

from ftplib import FTP
 
with FTP('ftp.example.com') as ftp:
    ftp.login('username', 'password')
    
    # Detailed listing (Unix ls -l style)
    ftp.dir()
    
    # Simple list of names
    files = ftp.nlst()
    print(files)
    
    # Parse detailed listing
    file_list = []
    ftp.dir(file_list.append)
    for line in file_list:
        print(line)

MLSD (Modern Listing)

from ftplib import FTP
 
with FTP('ftp.example.com') as ftp:
    ftp.login('username', 'password')
    
    # Machine-readable listing
    for name, facts in ftp.mlsd():
        print(f"{name}: {facts}")
        # facts = {'type': 'file', 'size': '1234', 'modify': '...'} 

Anonymous FTP

from ftplib import FTP
 
with FTP('ftp.gnu.org') as ftp:
    ftp.login()  # Anonymous login
    ftp.cwd('/gnu')
    ftp.dir()

Progress Tracking

from ftplib import FTP
 
def download_with_progress(ftp, remote_path, local_path):
    # Get file size
    size = ftp.size(remote_path)
    downloaded = 0
    
    def callback(data):
        nonlocal downloaded
        downloaded += len(data)
        percent = (downloaded / size) * 100
        print(f"\rDownloading: {percent:.1f}%", end='')
        return data
    
    with open(local_path, 'wb') as f:
        def write_callback(data):
            f.write(callback(data))
        ftp.retrbinary(f'RETR {remote_path}', write_callback)
    print()  # Newline
 
with FTP('ftp.example.com') as ftp:
    ftp.login('user', 'pass')
    download_with_progress(ftp, 'large_file.zip', 'local.zip')

Passive Mode

from ftplib import FTP
 
with FTP('ftp.example.com') as ftp:
    ftp.login('username', 'password')
    
    # Enable passive mode (usually default)
    ftp.set_pasv(True)
    
    # Active mode (for servers that require it)
    ftp.set_pasv(False)

Timeout and Error Handling

from ftplib import FTP, error_perm, error_temp
import socket
 
try:
    with FTP('ftp.example.com', timeout=30) as ftp:
        ftp.login('username', 'password')
        ftp.retrbinary('RETR file.zip', open('file.zip', 'wb').write)
except error_perm as e:
    print(f"Permission error: {e}")
except error_temp as e:
    print(f"Temporary error: {e}")
except socket.timeout:
    print("Connection timed out")
except ConnectionRefusedError:
    print("Could not connect to server")

Sync Directory

from ftplib import FTP
from pathlib import Path
 
def upload_directory(ftp, local_dir, remote_dir):
    """Upload a local directory to FTP."""
    local_path = Path(local_dir)
    
    try:
        ftp.mkd(remote_dir)
    except:
        pass
    
    ftp.cwd(remote_dir)
    
    for item in local_path.iterdir():
        if item.is_file():
            with open(item, 'rb') as f:
                ftp.storbinary(f'STOR {item.name}', f)
            print(f"Uploaded: {item.name}")
        elif item.is_dir():
            upload_directory(ftp, item, item.name)
    
    ftp.cwd('..')
 
with FTP('ftp.example.com') as ftp:
    ftp.login('user', 'pass')
    upload_directory(ftp, './website', '/public_html')

Debug Mode

from ftplib import FTP
 
with FTP('ftp.example.com') as ftp:
    ftp.set_debuglevel(2)  # Show all FTP commands
    ftp.login('username', 'password')
    ftp.dir()

Summary

ftplib provides full FTP client functionality:

  • FTP() for plain connections, FTP_TLS() for secure
  • retrbinary()/retrlines() for downloads
  • storbinary()/storlines() for uploads
  • cwd(), mkd(), rmd() for directories
  • nlst(), dir(), mlsd() for listings

FTP is legacy but still common for file hosting and deployment. Consider SFTP (paramiko) for new projects.

React to this post: