Python FTP Client Tutorial

Advertisement

Advertisement

Introduction

FTP or File Transfer Protocol is a common way to transfer files. For FTP, Python has a built in package called ftplib.

There is also a Secure File Transfer Protocol (SFTP) that uses SSH to encrypt the communication. We will focus just on traditional FTP in this guide. For SFTP you can check out the Paramiko package.

In this guide, we will look at some basic operations like:

  • Connect and login to an FTP server
  • List directories and files
  • Upload and download files

Note these examples are all using Python 3.

Connect to an FTP server

The first thing to learn is how to connect to an FTP server. We'll look at how to connect anonymously and

  • Port 21 - Default FTP port

Anonymous connection

This first example shows how to use a with context manager to login to an FTP server. The connection will automatically be closed. The code will print out the welcome banner just to ensure it connected.

# Anonymous FTP login
from ftplib import FTP

with FTP('ftp.example.com') as ftp:
    print(ftp.getwelcome())

Authenticated login

If you want to authenticate, you can simply pass the user and passwd parameters to the FTP() constructor or you can call connect() and login() yourself. This example shows how to login using both methods.

from ftplib import FTP

# Connect and login at once
with FTP(host='ftp.example.com', user='me', passwd='secret') as ftp:
    print(ftp.getwelcome())

# Or connect and login as separate steps
ftp = FTP()
ftp.connect('ftp.example.com', 21)
ftp.login('me', 'secret')
print(ftp.getwelcome())
ftp.close()

Connect with SSL/TLS

Use the ftplib.FTP_TLS class instead. Note, this is not SFTP that uses SSH over port 22, this is FTP with SSL/TLS over port 21. If your provider offers this option, always use it over the plaintext FTP.

Then make sure you call ftplib.FTP_TLS.prot_p() which will setup the secure data connection.

from ftplib import FTP_TLS

# Connect to server using TLS, port 21 default. Is not SFTP over SSH
with FTP_TLS('ftp.example.com', 'user', 'secret') as ftp:
    print(ftp.getwelcome())

Work with directories

Let's look first at how to do some basic operations with directories like:

  • printing your current working directory
  • changing directories
  • creating a directory
  • removing a directory

Print current working directory

Once you are connected, you will first want to know where you are in the directory structure. The pwd() function on the ftplib.FTP object provides this data.

from ftplib import FTP

with FTP('ftp.example.com', 'user', 'secret') as ftp:
    print(ftp.pwd())  # Usually default is /

Create a directory

You can make a directory using ftplib.FTP.mkd() and pass it the name of the directory. It will return a string containing the name of the directory created.

from ftplib import FTP

with FTP('ftp.example.com', 'user', 'secret') as ftp:
    ftp.mkd('my_dir')

Remove a directory

To remove a directory, just use rmd() on your FTP object. A directory must be empty to delete it.

from ftplib import FTP

with FTP('ftp.example.com', 'user', 'secret') as ftp:
    ftp.rmd('my_dir')

Change current working directory

To switch to a different directory, use ftplib.FTP.cwd().

from ftplib import FTP

with FTP('ftp.example.com', 'user', 'secret') as ftp:
    print(ftp.cwd('other_dir'))  # Change to `other_dir/`

List directory contents

The next basic task you will probably want to do is list the files in a directory. The ftplib.FTP.dir() command will list all the files in your current directory.

It does not just provide the filename though, it provides a string that contains the permissions, whether it is a directory, byte size, modification timestamp, owner, and group information. It is formatted just like the output from an ls command. Since the output is a string, you will have to parse out the information from it manually using split() or regular expressions.

from ftplib import FTP

with FTP('ftp.example.com', 'user', 'secret') as ftp:
    # List files
    files = []
    ftp.dir(files.append)  # Takes a callback for each file
    for f in files:
        print(f)

An example file listing might look similar too:

drwxr-xr-x  3 dano   dano  4096 Mar 12 23:15 www-null

Work with files

Now that we have learned how to navigate directories, it is time to learn how to:

  • upload files
  • download files
  • get the size of a file
  • rename a file
  • delete a file

Upload a file

You may not be able to upload a file on every server, especially if you are only logged in anonymously. If you do have the permissions though, this example will show you how to upload a file.

For text files use storlines() and for binary use storbinary().

from ftplib import FTP

with FTP('ftp.example.com', 'user', 'pass') as ftp:

    # For text or binary file, always use `rb`
    with open('test.txt', 'rb') as text_file:
        ftp.storlines('STOR test.txt', text_file)
    with open('image.png', 'rb') as image_file:
        ftp.storbinary('STOR image.png', image_file)

Get the size of a file

To check the size of a file on a remote FTP server, you can simply use the size() function as demonstrated in the following example.

Depending on whether you want to check a text file or a binary file, you want to tell the FTP server what type you want to use. Use sendcmd() and pass the type with either TYPE I for image/binary data or TYPE A for ASCII text.

The size() function will return the size of the file in bytes.

from ftplib import FTP, all_errors

with FTP('ftp.example.com', 'user', 'pass') as ftp:
    try:
        ftp.sendcmd('TYPE I')  # "Image" or binary data
        print(ftp.size('image.png')) # Get size of 'image.png' on server
    except all_errors as error:
        print(f"Error checking image size: {error}")

    try:
        ftp.sendcmd('TYPE A')  # "ASCII" text
        print(ftp.size('test.txt'))
    except all_errors as error:
        print(f"Error checking text file size: {error}")

Rename a file

To rename a file on a remote FTP server, use the rename() function and pass the original filename and the new filename.

from ftplib import FTP, all_errors

with FTP('ftp.example.com', 'user', 'pass') as ftp:
    try:
        ftp.rename('test.txt', 'my_file.txt')
    except all_errors as error:
        print(f'Error renaming file on server: {error}')

Download a file

To download a file you can use retrlines() or retrbinary() on the FTP object.

FTP.retrlines(cmd, callback=None)
FTP.retrbinary(cmd, callback, blocksize=8192, rest=None)

For the callback, we'll use write() on the file object so each chunk is written to the file we have open.

from ftplib import FTP, all_errors

with FTP('ftp.example.com', 'user', 'pass') as ftp:

    # For text files
    with open('local_test.txt', 'w') as local_file:  # Open local file for writing
        # Download `test.txt` from server and write to `local_file`
        # Pass absolute or relative path
        response = ftp.retrlines('RETR test.txt', local_file.write)

        # Check the response code
        # https://en.wikipedia.org/wiki/List_of_FTP_server_return_codes
        if response.startswith('226'):  # Transfer complete
            print('Transfer complete')
        else:
            print('Error transferring. Local file may be incomplete or corrupt.')

    # For binary use `retrbinary()`
    with open('image.png', 'wb') as local_file:
        ftp.retrbinary('RETR image.png', local_file.write)

Delete a file

To delete a remote file, use the delete() function and give it the filename you want to delete. You cannot delete directories with this. For directories, you must use rmd() as shown in the example earlier.

from ftplib import FTP, all_errors

with FTP('ftp.example.com', 'user', 'pass') as ftp:
    try:
        ftp.delete('test.txt')
    except all_errors as error:
        print(f'Error deleting file: {error}') 

Error checking

So far, none of the examples here include any exception handling. There are a few exceptions that may be thrown if the server returns an error or malformed response.

To catch everything, wrap your code with a try statement and catch all FTP exceptions with ftplib.all_errors.

Some of the exceptions to watch for are:

  • ftplib.error_reply - unexpected server reply
  • ftplib.error_temp - temporary error
  • ftplib.error_perm - permanent error
  • ftplib.error_proto - malformed server reply
  • ftplib.all_errors - catches all above errors that a server can return and OSError and EOFError.
from ftplib import FTP

with FTP('ftp.example.com', 'user', 'secret') as ftp:
    try:
        print(ftp.pwd())
    except ftplib.all_errors as e:
        print(f'Error with FTP: {e}')

Conclusion

After reading this guide, you should understand how to connect to an FTP server with or without TLS and login anonymously or as an authenticated user. You should also understand how to move around directories, create and delete directories, list files, and upload and download files from FTP.

References

Advertisement

Advertisement