GeezerVisonThe World Wide Web, restored to 800 pixels of glory. |
| Home|About|Gaming cabinet|Archives|How it works|Starter sites |
GeezerVison is showing zetcode.com — Python FTP programming - Python ftplib Python FTPlast modified October 30, 2026 Python 3.15
In this article we show how to work with FTP in Python using the
FTP
File Transfer Protocol (FTP) is a standard network protocol used
for transferring computer files between a client and server on a computer
network. Clients and servers communicate with a set of FTP commands, such
as
Many servers provide FTP services with anonymous access, which allows
downloading files without an account. Plain FTP sends passwords and data
unencrypted. For sensitive data, FTP over TLS (the
Python ftplib
Python
The Local FTP server
Public anonymous FTP servers have become rare; for instance, Debian shut
down its FTP service in 2017. To have a predictable test environment, we
run a local FTP server written in Python with the
$ pip install pyftpdlib Running the serverThe server has a user account and anonymous access. ftp_server.py
from pyftpdlib.authorizers import DummyAuthorizer
from pyftpdlib.handlers import FTPHandler
from pyftpdlib.servers import FTPServer
authorizer = DummyAuthorizer()
authorizer.add_user('user7', 's$cret', 'ftproot/home/user7', perm='elradfmw')
authorizer.add_anonymous('ftproot/pub')
handler = FTPHandler
handler.authorizer = authorizer
handler.banner = 'Welcome to the ZetCode test FTP server'
server = FTPServer(('127.0.0.1', 2121), handler)
server.serve_forever()
The $ python ftp_server.py & [I 2026-10-30 21:30:43] concurrency model: async [I 2026-10-30 21:30:43] masquerade (NAT) address: None [I 2026-10-30 21:30:43] passive ports: None [I 2026-10-30 21:30:43] >>> starting FTP server on 127.0.0.1:2121, pid=169146 <<< Welcome message
The welcome.py
import ftplib
with ftplib.FTP() as ftp:
print(ftp.connect('localhost', 2121))
print(ftp.getwelcome())
The $ python welcome.py 220 Welcome to the ZetCode test FTP server 220 Welcome to the ZetCode test FTP server Directory listing
The listing.py
import ftplib
with ftplib.FTP() as ftp:
try:
ftp.connect('localhost', 2121)
ftp.login()
files = []
ftp.dir(files.append)
for line in files:
print(line)
except ftplib.all_errors as e:
print('FTP error:', e)
When the $ python listing.py -rw-rw-r-- 1 user user 94 Oct 30 20:28 README drwxrwxr-x 2 user user 4096 Oct 30 20:28 docs -rw-rw-r-- 1 user user 74 Oct 30 20:29 words.txt.gz Machine-readable listing with MLSD
The mlsd.py
import ftplib
with ftplib.FTP() as ftp:
try:
ftp.connect('localhost', 2121)
ftp.login()
for name, facts in ftp.mlsd('/', facts=['type', 'size', 'modify']):
size = facts.get('size', '-')
print(f"{facts['type']:<5} {size:>6} {facts['modify']} {name}")
except ftplib.all_errors as e:
print('FTP error:', e)
The method yields tuples of a file name and a dictionary of facts. With
the $ python mlsd.py file 94 20261030202858 README file 74 20261030202910 words.txt.gz dir 4096 20261030202858 docs FTP commands
The FTP client sends commands to the FTP server, such as ftp_commands.py
import ftplib
with ftplib.FTP() as ftp:
try:
ftp.connect('localhost', 2121)
ftp.login()
resp = ftp.sendcmd('PWD')
print(resp)
print(ftplib.parse257(resp))
print(ftp.pwd())
print(ftp.sendcmd('SYST'))
print(ftp.voidcmd('NOOP'))
except ftplib.all_errors as e:
print('FTP error:', e)
We send the $ python ftp_commands.py 257 "/" is the current directory. / / 215 UNIX Type: L8 200 I successfully did nothing'. Changing directory
The change_directory.py
import ftplib
with ftplib.FTP() as ftp:
try:
ftp.connect('localhost', 2121)
ftp.login()
print(ftp.pwd())
ftp.cwd('docs')
print(ftp.pwd())
print(ftp.nlst())
ftp.cwd('..')
print(ftp.pwd())
ftp.cwd('photos')
except ftplib.all_errors as e:
print('FTP error:', e)
We change to the $ python change_directory.py / /docs ['users.csv', 'words.txt'] / FTP error: 550 No such file or directory. Getting size of a file
The file_size.py
import ftplib
with ftplib.FTP() as ftp:
try:
ftp.connect('localhost', 2121)
ftp.login()
try:
print(ftp.size('README'))
except ftplib.error_perm as e:
print(e)
ftp.voidcmd('TYPE I')
print(ftp.size('README'))
print(ftp.size('words.txt.gz'))
except ftplib.all_errors as e:
print('FTP error:', e)
The server refuses the $ python file_size.py 550 SIZE not allowed in ASCII mode. 94 74 Downloading a text file
To download a text file, we use the download_text_file.py
import ftplib
import os
file_orig = 'README'
file_copy = 'README.txt'
with ftplib.FTP() as ftp:
try:
ftp.connect('localhost', 2121)
ftp.login()
with open(file_copy, 'w') as f:
res = ftp.retrlines(f'RETR {file_orig}',
lambda line: f.write(line + '\n'))
print(res)
except ftplib.all_errors as e:
print('FTP error:', e)
if os.path.isfile(file_copy):
os.remove(file_copy)
The $ python download_text_file.py 226 Transfer complete. $ cat README.txt ZetCode test FTP server This directory contains sample files for the Python ftplib tutorial. Downloading a binary file
Binary files are downloaded with the download_binary_file.py
import ftplib
import gzip
file_orig = 'words.txt.gz'
with ftplib.FTP() as ftp:
try:
ftp.connect('localhost', 2121)
ftp.login()
with open(file_orig, 'wb') as f:
res = ftp.retrbinary(f'RETR {file_orig}', f.write)
print(res)
except ftplib.all_errors as e:
print('FTP error:', e)
with gzip.open(file_orig, 'rt') as f:
lines = f.readlines()
print(len(lines), 'lines')
print(lines[:5])
The $ python download_binary_file.py 226 Transfer complete. 1000 lines ['sky\n', 'blue\n', 'falcon\n', 'owl\n', 'cloud\n'] Login error
The login_error.py
import ftplib
with ftplib.FTP() as ftp:
ftp.connect('localhost', 2121)
try:
ftp.login('user7', 'secret')
except ftplib.error_perm as e:
print('Login failed:', e)
print(ftp.login('user7', 's$cret'))
The first attempt fails with the 530 reply code. The connection stays open, so we can try again with the correct password. $ python login_error.py Login failed: 530 Authentication failed. 230 Login successful. Creating directories
A directory is created with the create_directory.py
import ftplib
with ftplib.FTP() as ftp:
try:
ftp.connect('localhost', 2121)
ftp.login('user7', 's$cret')
print(ftp.mkd('newdir'))
print(ftp.mkd('newdir/sub'))
ftp.retrlines('LIST')
except ftplib.all_errors as e:
print('FTP error:', e)
We log in as $ python create_directory.py /newdir /newdir/sub drwxrwxr-x 3 user user 4096 Oct 30 20:30 newdir Uploading files
Files are uploaded with the upload_files.py
import ftplib
with ftplib.FTP() as ftp:
try:
ftp.connect('localhost', 2121)
ftp.login('user7', 's$cret')
with open('notes.txt', 'rb') as f:
print(ftp.storlines('STOR notes.txt', f))
with open('words.txt.gz', 'rb') as f:
print(ftp.storbinary('STOR newdir/words.txt.gz', f))
for name, facts in ftp.mlsd('newdir', facts=['type', 'size']):
print(name, facts)
except ftplib.all_errors as e:
print('FTP error:', e)
Both methods read the data from a file object opened in binary mode. We
upload a local text file to the home directory of the user and the
downloaded gzip file to the $ python upload_files.py
226 Transfer complete.
226 Transfer complete.
words.txt.gz {'size': '74', 'type': 'file'}
sub {'size': '4096', 'type': 'dir'}
Renaming and deleting
The rename_delete.py
import ftplib
with ftplib.FTP() as ftp:
try:
ftp.connect('localhost', 2121)
ftp.login('user7', 's$cret')
print(ftp.rename('notes.txt', 'falcon.txt'))
print(ftp.delete('newdir/words.txt.gz'))
print(ftp.rmd('newdir/sub'))
print(ftp.rmd('newdir'))
print(ftp.nlst())
except ftplib.all_errors as e:
print('FTP error:', e)
We rename the uploaded text file, delete the binary file, and remove the two directories; the subdirectory has to be removed first. Finally, we list the names in the home directory. $ python rename_delete.py 250 Renaming ok. 250 File removed. 250 Directory removed. 250 Directory removed. ['falcon.txt'] Sourceftplib - FTP protocol client - Python documentation
In this article we have worked with Python AuthorMy name is Jan Bodnar, and I am a passionate programmer with extensive programming experience. I have been writing programming articles since 2007. To date, I have authored over 1,400 articles and 8 e-books. I possess more than ten years of experience in teaching programming. List all Python tutorials. |
| You are visitor 01402520 — GeezerVison does not store the pages you look up. Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required. |