ftp server using python

This blog will teach you how to download and upload files in an FTP server using python with a Python FTP client. Before moving further, let’s briefly examine what FTP is.

 

What Is FTP? How File Transfer Protocol Works Explained

FTP stands for Files Transfer Protocol and is widely used for secure file transfer between systems. FTP’s main feature is transferring files from local to remote using TCP connections. FTP is also called application layer protocol. FTP has two processes for the transforming process: data connection and control connection.

As per today’s topic, we will use the ftplib module of python in the FTP server for downloading and uploading files through Python automation scripts. Let’s have a brief about the ftplib module.

 

What Is Python’s ftplib Module and How Do You Use It?

Python has a module called ftplib to transfer files in the FTP server for efficient FTP file management. With the help of ftplib, you can create a remote server connection to the client-side FTP server and transfer files. As a Django development company, you might frequently need to upload and download files in your projects, making this module indispensable. Also, you can download and upload files in the FTP server using Python’s ftplib module.

Is FTP Secure? When to Use FTPS or SFTP Instead

Plain FTP transmits credentials and data in cleartext, making it vulnerable to packet sniffing and man-in-the-middle attacks. In 2026, most cloud providers and compliance frameworks, including PCI-DSS and HIPAA, explicitly discourage plain FTP for any sensitive file transfer. Python’s `ftplib` does support FTPS (FTP over TLS) via `ftplib.FTP_TLS`, which encrypts both the control and data channels. For environments requiring key-based authentication and stronger security, SFTP (SSH File Transfer Protocol) via the `paramiko` library is the industry standard. If your use case involves anything beyond internal test servers or non-sensitive data, evaluate FTPS or SFTP before deploying plain FTP in production.

 

Step-by-Step FTP File Transfer Implementation Using Python ftplib

Here, we showcase the process using a test FTP server, DLPTEST, and Python’s built-in ftplib module for Python file transfer. For any Python development company aiming to provide robust solutions, understanding how to work with FTP servers is vital. By following this guide, you’ll be well-equipped to handle file transfers efficiently in your projects.

import ftplib

FTP_HOST = "ftp.dlptest.com"
FTP_USER = "<host_username>";
FTP_PASS = "password"
</host_username>

The above code tends to take credentials to the FTP_User for FTP authentication. The password can be changed from time to time. While making changes, make sure you are visiting their website to correct it.

The following step code is to establish the connection with FTP_SERVER.

#connect to FTP Server

ftp = ftplib.FTP(FTP_HOST, FTP_USER, FTP_PASS)
ftp.encoding = "utf-8"

 

Encoding Pitfalls with ftp.encoding

The line ftp.encoding = "utf-8" appears in the post’s code examples and is easy to copy without fully understanding what it controls. In ftplib, the encoding attribute applies to the control channel — specifically to commands sent as text strings, such as the filename passed in the STOR or RETR command string. It does not encode or decode the data channel, which transfers raw bytes regardless of this setting.

Where this causes real problems: if you are uploading a file whose name contains non-ASCII characters (accented letters, CJK characters, or Cyrillic), the STOR command string must be encoded in a charset the FTP server accepts. Many older FTP servers default to Latin-1 rather than UTF-8, which means setting ftp.encoding = "utf-8" can cause a ftplib.error_perm: 553 File name not allowed error on servers that do not support UTF-8 filenames. On such servers, setting ftp.encoding = "latin-1" resolves the issue.

For binary file contents — the actual bytes written to disk — the encoding attribute has zero effect. The data passes through storbinary() and retrbinary() as raw bytes, so text encoding of file contents remains your responsibility at the Python level before the file is opened for transfer.

 

How to Upload a File to an FTP Server Using Python’s STOR Command

Uploading the file using FTP Server is shown below. The local name to identify the file is “Some_file”.

filename = "some_file.txt"
with open(filename, "rb") as file:
    # use FTP's STOR command to upload the file
    ftp.storbinary(f"STOR {filename}", file)

After this code, the file starts to upload to FTP_SERVER for automated file uploads. And to upload, we have used the STOR command.
Also, “rb” opens the file as read-only in binary format and starts reading from the beginning of the file for binary file transfer. While the binary form can be used for various purposes.
As we are aware that the test server will delete the files after 30 minutes, so to make sure the files are uploaded, we will list the files and directories using the below code.

# list current files &amp; directories
ftp.dir()

Transferring Multiple Files with ftplib

Uploading or downloading a single file works for simple scripts, but most automation pipelines need to move entire directories or filtered file sets in one run. Here is how to handle bulk transfers reliably using ftplib:

  1. List the target directory contents — call ftp.nlst() to retrieve a list of filenames from the remote directory as plain strings. Unlike ftp.dir(), nlst() returns a list you can iterate, filter, and pass directly to retrbinary().
  2. Filter by file type or pattern — use Python’s fnmatch module or a simple str.endswith() check to narrow the list to the file types you need (e.g., only .csv or .log files), avoiding accidental downloads of hidden files or directories.
  3. Loop and transfer with error isolation — wrap each retrbinary() or storbinary() call in its own try/except ftplib.all_errors block so a single failed file doesn’t abort the entire batch.
  4. Preserve the remote directory structure locally — use os.makedirs(local_dir, exist_ok=True) before opening the local file for writing; without this, open() raises a FileNotFoundError when the local path doesn’t exist.
  5. Log transfer outcomes per file — record the filename and success/failure status to a log file or stdout; for long-running batches, this is the only way to diagnose partial failures without re-running the entire transfer.

 

How to Download a File from an FTP Server Using Python’s RETR Command

filename = "some_file.txt"
with open(filename, "wb") as file:
    # use FTP's RETR command to download the file
    ftp.retrbinary(f"RETR {filename}", file.write)

We will download with the command “wb”, as it will write the file from FTP_SERVER to the local machine.

Here, “wb” opens the file as write-only in binary format.

Using the RETR command, which downloads a copy of a file from the server, we can request a copy of a file by giving the command the name of the file we wish to download as the first argument.

The second argument to the ftp.retrbinary() method specifies the procedure when saving the file to the local machine.

The file can reappear even after deleting it as we rerun the code, proving the successful downloading process.

Now to close the FTP server connection, the last code will be:

#quit and close the connection
ftp.quit()

Complete Python Code Examples: FTP Upload and Download with ftplib

Upload file in FTP Server using Python.

Ftp_file_uploader.py

import ftplib
#FTP Server Crendentials
FTP_HOST = "ftp.dlptest.com"
FTP_USER = "<host_username>"
FTP_PASS = "password"

#connect to FTP Server
ftp = ftplib.FTP(FTP_HOST, FTP_USER, FTP_PASS)
ftp.encoding = "utf-8"

# local file name you want to upload
filename = "some_file.txt"
with open(filename, "rb") as file:
    # use FTP's STOR command to upload the file
    ftp.storbinary(f"STOR {filename}", file)

# list current files &amp; directories
ftp.dir()

# quit and close the connection
ftp.quit()s
</host_username>

Download file in FTP Server using Python.

Common ftplib Errors in Python and How to Fix Them

Developers working with `ftplib` frequently encounter a handful of predictable errors. `ftplib.error_perm: 530 Login incorrect` means the FTP credentials are wrong or have expired — for test servers like DLPTEST, passwords rotate regularly, so always verify on their site. `ConnectionRefusedError: [Errno 111]` indicates the FTP host is unreachable, often due to a firewall rule or incorrect `FTP_HOST` value. `ftplib.error_temp: 421 Service not available` typically means the server has hit its connection limit; adding a retry loop with `time.sleep()` resolves this in most cases. Wrapping your FTP calls in a `try/except ftplib.all_errors` block is the recommended pattern for any production-facing transfer script.

 

FTP Transfer Resumption and Partial Failures

Plain ftplib does not support resuming an interrupted transfer out of the box. If a storbinary() or retrbinary() call is cut off mid-file — due to a network timeout, server disconnect, or script crash — the partial file is left on the destination with no built-in checkpoint. For scripts running against reliable internal servers this is rarely a problem, but for long-running transfers over unstable connections it becomes the most expensive failure mode to debug. Understanding what ftplib does and does not handle lets you build the right recovery logic before you need it in production.

Key facts about transfer interruption in ftplib:

  • retrbinary() writes to a local file handle sequentially — if the connection drops mid-transfer, the local file contains a truncated copy with no indicator that it is incomplete; you must validate file size or checksum after the fact.
  • storbinary() does not report partial upload success — the server may have received 80% of a file before the connection dropped, and ftplib will raise an exception without telling you how many bytes were committed.
  • The FTP REST command enables resume, but ftplib requires manual implementation — call ftp.sendcmd('REST <offset>') before retrbinary() to start from a byte offset; you must calculate the offset yourself by checking local file size with os.path.getsize().
  • Timeout settings on the FTP connection affect failure detection speed — pass a timeout argument to ftplib.FTP() (e.g., timeout=30) so hung transfers raise a socket.timeout instead of blocking indefinitely.
  • For large or mission-critical file moves, checksum verification after transfer is the only reliable confirmation — compare os.path.getsize() against the remote size from ftp.size(filename) as a minimum integrity check.

 

Conclusion:

Wrapping up here with the above-given codes to upload and download files in FTP Server using Python for Python server automation. To conclude, the code is running perfectly with the output shared above.