杰瑞科技汇

python ftp client

Of course! Here is a comprehensive guide to creating a Python FTP client, covering everything from basic file transfers to best practices and modern alternatives.

We'll use Python's built-in ftplib module, which is perfect for this task.

The Basics: Connecting to an FTP Server

First, you need to connect to the server. The core components are:

  • ftplib.FTP(): Creates a connection object.
  • login(): Authenticates with a username and password.
  • quit(): Closes the connection gracefully.
import ftplib
# --- Configuration ---
# Replace with your FTP server details
FTP_HOST = "ftp.dlptest.com"
FTP_USER = "dlpuser"
FTP_PASS = "rNrKYTX9g7z3RgJR"
try:
    # 1. Create a FTP client object
    ftp = ftplib.FTP(FTP_HOST, FTP_USER, FTP_PASS)
    # 2. Check if connection was successful
    print(f"Successfully connected to {FTP_HOST}")
    print(ftp.getwelcome()) # Prints the welcome message from the server
    # 3. List files in the current directory
    print("\nFiles in the root directory:")
    ftp.retrlines('LIST') # LIST is the command to list directory contents
    # 4. Quit the session
    ftp.quit()
    print("\nConnection closed.")
except ftplib.all_errors as e:
    print(f"FTP Error: {e}")

Explanation:

  • ftplib.all_errors is a base class for all exceptions raised by the ftplib module. It's good practice to catch this to handle potential connection issues, authentication failures, etc.
  • ftp.retrlines('LIST') is a convenient way to list files. It sends the LIST command and prints each line of the response.

Uploading a File (Upload)

To upload a file, you use the storbinary() method. It sends a local file in binary mode to the server.

import ftplib
import os
# --- Configuration ---
FTP_HOST = "ftp.dlptest.com"
FTP_USER = "dlpuser"
FTP_PASS = "rNrKYTX9g7z3RgJR"
LOCAL_FILE_PATH = "my_local_file.txt"  # File to upload
REMOTE_DIR = "/incoming/"              # Directory on the server to upload to
try:
    ftp = ftplib.FTP(FTP_HOST, FTP_USER, FTP_PASS)
    print(f"Connected to {FTP_HOST}")
    # --- Optional: Change to a specific directory on the server ---
    # If the directory doesn't exist, this will fail.
    # You might need to create it first (see section below).
    ftp.cwd(REMOTE_DIR)
    print(f"Changed directory to: {ftp.pwd()}") # pwd() prints current working directory
    # --- Upload the file ---
    # The 'STOR' command is used to store a file.
    # 'storbinary' handles the file in binary mode.
    with open(LOCAL_FILE_PATH, 'rb') as file: # IMPORTANT: Open in binary mode ('rb')
        ftp.storbinary(f'STOR {os.path.basename(LOCAL_FILE_PATH)}', file)
    print(f"Successfully uploaded {LOCAL_FILE_PATH} to {ftp.pwd()}")
    ftp.quit()
except ftplib.all_errors as e:
    print(f"FTP Error: {e}")

Key Points:

  • with open(...): This ensures the local file is properly closed after the upload.
  • 'rb' mode: You must open the local file in binary read mode ('rb') for storbinary.
  • os.path.basename(): This gets just the filename from the full local path, preventing you from creating subdirectories on the server unintentionally.
  • ftp.cwd(): Use cwd (Change Working Directory) to navigate the server's filesystem.

Downloading a File (Download)

Downloading is the reverse of uploading. You use retrbinary() to retrieve a file in binary mode and save it locally.

import ftplib
import os
# --- Configuration ---
FTP_HOST = "ftp.dlptest.com"
FTP_USER = "dlpuser"
FTP_PASS = "rNrKYTX9g7z3RgJR"
REMOTE_FILE_NAME = "README.txt"  # File to download
LOCAL_DIR = "./downloads/"       # Local directory to save the file
try:
    ftp = ftplib.FTP(FTP_HOST, FTP_USER, FTP_PASS)
    print(f"Connected to {FTP_HOST}")
    # --- Optional: Navigate to the remote directory ---
    # ftp.cwd("/some/remote/path/")
    # --- Ensure the local directory exists ---
    os.makedirs(LOCAL_DIR, exist_ok=True)
    # --- Download the file ---
    # The 'RETR' command is used to retrieve a file.
    # 'retrbinary' handles the file in binary mode.
    local_path = os.path.join(LOCAL_DIR, REMOTE_FILE_NAME)
    with open(local_path, 'wb') as file: # IMPORTANT: Open in binary write mode ('wb')
        ftp.retrbinary(f'RETR {REMOTE_FILE_NAME}', file.write)
    print(f"Successfully downloaded {REMOTE_FILE_NAME} to {local_path}")
    ftp.quit()
except ftplib.all_errors as e:
    print(f"FTP Error: {e}")
except FileNotFoundError:
    print(f"Error: The file {REMOTE_FILE_NAME} was not found on the server.")

Key Points:

  • 'wb' mode: You must open the local file in binary write mode ('wb') for retrbinary.
  • os.makedirs(..., exist_ok=True): This is a robust way to create the local download directory if it doesn't already exist. It won't raise an error if the directory is already there.

Useful FTP Commands and Operations

Here are other common tasks you'll perform.

A. Listing Directories

We've seen LIST, but NLST is another option.

# Using LIST (detailed, like `ls -l`)
print("--- Detailed List ---")
ftp.retrlines('LIST')
# Using NLST (simple, like `ls`)
print("\n--- Simple List ---")
ftp.retrlines('NLST')

B. Creating and Deleting Directories

# Create a directory
ftp.mkd('new_folder_name')
# Remove a directory (must be empty)
ftp.rmd('new_folder_name')

C. Deleting a File

# Delete a file
ftp.delete('file_to_delete.txt')

D. Renaming a File

# Rename a file from 'old_name' to 'new_name'
ftp.rename('old_name.txt', 'new_name.txt')

A Complete, Reusable FTP Client Class

For real-world applications, wrapping the logic in a class is much cleaner and more reusable.

import ftplib
import os
class FTPClient:
    def __init__(self, host, user, passwd):
        self.host = host
        self.user = user
        self.passwd = passwd
        self.ftp = None
    def connect(self):
        """Connects to the FTP server."""
        try:
            self.ftp = ftplib.FTP(self.host, self.user, self.passwd)
            print(f"Connected to {self.host}")
            return True
        except ftplib.all_errors as e:
            print(f"Connection failed: {e}")
            return False
    def disconnect(self):
        """Closes the FTP connection."""
        if self.ftp:
            try:
                self.ftp.quit()
                print("Disconnected from server.")
            except ftplib.all_errors as e:
                print(f"Error during disconnect: {e}")
    def upload_file(self, local_path, remote_path=None):
        """Uploads a file to the server."""
        if not self.ftp:
            print("Not connected to server.")
            return False
        if not os.path.exists(local_path):
            print(f"Local file not found: {local_path}")
            return False
        remote_path = remote_path or os.path.basename(local_path)
        try:
            with open(local_path, 'rb') as file:
                self.ftp.storbinary(f'STOR {remote_path}', file)
            print(f"Uploaded {local_path} to {remote_path}")
            return True
        except ftplib.all_errors as e:
            print(f"Upload failed: {e}")
            return False
    def download_file(self, remote_path, local_path):
        """Downloads a file from the server."""
        if not self.ftp:
            print("Not connected to server.")
            return False
        try:
            os.makedirs(os.path.dirname(local_path), exist_ok=True)
            with open(local_path, 'wb') as file:
                self.ftp.retrbinary(f'RETR {remote_path}', file.write)
            print(f"Downloaded {remote_path} to {local_path}")
            return True
        except ftplib.all_errors as e:
            print(f"Download failed: {e}")
            return False
# --- How to use the class ---
if __name__ == "__main__":
    HOST = "ftp.dlptest.com"
    USER = "dlpuser"
    PASS = "rNrKYTX9g7z3RgJR"
    client = FTPClient(HOST, USER, PASS)
    if client.connect():
        # Upload a file
        # Create a dummy file to upload
        with open("test_upload.txt", "w") as f:
            f.write("Hello from Python FTP Client!")
        client.upload_file("test_upload.txt", "/incoming/test_upload.txt")
        # Download a file
        client.download_file("/README.txt", "downloads/README.txt")
        client.disconnect()

Security: FTP vs. FTPS

Standard FTP is insecure! Your username, password, and all file data are sent in plain text. For any real application, you should use FTPS (FTP Secure), which wraps the FTP session in an SSL/TLS layer.

ftplib supports this easily.

Using Implicit FTPS (Common on older servers)

You connect to a different port (usually 990) and set up the secure connection immediately.

import ftplib
import ssl
# ... (host, user, passwd) ...
# Create a secure context
context = ssl.create_default_context()
try:
    # Connect using implicit FTPS on port 990
    with ftplib.FTP_TLS(host, user, passwd, context=context) as ftp:
        ftp.prot_p()  # Set up data connection to be secure
        print(f"Securely connected to {host}")
        # ... perform secure operations ...
        ftp.retrlines('LIST')
except ftplib.all_errors as e:
    print(f"FTPS Error: {e}")

Using Explicit FTPS (More modern and recommended)

You start with a normal FTP connection and then explicitly upgrade it to secure using the AUTH TLS command.

import ftplib
import ssl
# ... (host, user, passwd) ...
context = ssl.create_default_context()
try:
    # 1. Connect normally
    ftp = ftplib.FTP(host, user, passwd)
    print(f"Connected to {host}")
    # 2. Upgrade the control connection to TLS
    ftp.auth_tls(context=context)
    # 3. Set the data connection to be protected
    ftp.prot_p()
    print("Connection secured with TLS.")
    # ... now perform secure operations ...
    ftp.retrlines('LIST')
    ftp.quit()
except ftplib.all_errors as e:
    print(f"FTPS Error: {e}")

Modern Alternatives: SFTP and SCP

While FTP is still widely used for public file sharing, SFTP (SSH File Transfer Protocol) is the modern standard for secure, programmatic file transfer. It runs over a single SSH port and is generally considered more secure and easier to firewall than FTPS.

For SFTP in Python, the best library is paramiko.

Example using paramiko for SFTP:

First, install it: pip install paramiko

import paramiko
import os
# --- Configuration ---
SFTP_HOST = "sftp.example.com"
SFTP_PORT = 22
SFTP_USER = "your_username"
SFTP_PASS = "your_password" # Or use SSH keys for better security
REMOTE_PATH = "/remote/path/to/file.txt"
LOCAL_PATH = "local_download.txt"
try:
    # Create an SSH client
    ssh = paramiko.SSHClient()
    ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) # Note: This is not secure for production
    # Connect to the server
    ssh.connect(SFTP_HOST, port=SFTP_PORT, username=SFTP_USER, password=SFTP_PASS)
    # Create an SFTP client from the SSH session
    sftp = ssh.open_sftp()
    print(f"Successfully connected to {SFTP_HOST}")
    # Download a file
    sftp.get(REMOTE_PATH, LOCAL_PATH)
    print(f"Downloaded {REMOTE_PATH} to {LOCAL_PATH}")
    # Upload a file
    sftp.put("my_local_file.txt", "/remote/upload/my_local_file.txt")
    print("Uploaded my_local_file.txt")
    # Close the SFTP and SSH sessions
    sftp.close()
    ssh.close()
except paramiko.AuthenticationException:
    print("Authentication failed, please verify your credentials.")
except paramiko.SSHException as e:
    print(f"Unable to establish SSH connection: {e}")
except Exception as e:
    print(f"An error occurred: {e}")

Summary: FTP vs. SFTP

Feature FTP (ftplib) SFTP (paramiko)
Protocol Separate TCP connections (Control & Data) Runs over a single SSH connection
Security Insecure by default. Use FTPS for security. Secure by default. Encrypts everything.
Port 21 (FTP), 990 (Implicit FTPS) 22
Firewall Can be tricky (PASV/PORT modes) Very easy (single port)
Python Lib ftplib (built-in) paramiko (3rd party)
Use Case Legacy systems, public file downloads. Modern applications, secure server-to-server transfers.
分享:
扫描分享到社交APP
上一篇
下一篇