Send File via Socket

The following code establishes a client-server connection between two Python programs via the socket standard module and sends a file from the client to the server. The file transfer logic is contained in two functions: the client defines a send_file() function to send a file through a socket and inversely the server defines receive_file() to receive it. This way the functions can be easily moved from this post to other programs that already implement a client-server connection. The code is ready to send any file format and size. Python 3.8 or greater is required.

You can download both Python files and a test file to be transfered from here: send-file-via-socket.zip.

Server:

# server.py
import socket
import struct
def receive_file_size(sck: socket.socket):
    # This funcion makes sure that the bytes which indicate
    # the size of the file that will be sent are received.
    # The file is packed by the client via struct.pack(),
    # a function that generates a bytes sequence that
    # represents the file size.
    fmt = "<Q"
    expected_bytes = struct.calcsize(fmt)
    received_bytes = 0
    stream = bytes()
    while received_bytes < expected_bytes:
        chunk = sck.recv(expected_bytes - received_bytes)
        stream += chunk
        received_bytes += len(chunk)
    filesize = struct.unpack(fmt, stream)[0]
    return filesize
def receive_file(sck: socket.socket, filename):
    # First read from the socket the amount of
    # bytes that will be received from the file.
    filesize = receive_file_size(sck)
    # Open a new file where to store the received data.
    with open(filename, "wb") as f:
        received_bytes = 0
        # Receive the file data in 1024-bytes chunks
        # until reaching the total amount of bytes
        # that was informed by the client.
        while received_bytes < filesize:
            chunk = sck.recv(1024)
            if chunk:
                f.write(chunk)
                received_bytes += len(chunk)
with socket.create_server(("localhost", 6190)) as server:
    print("Waiting for the client...")
    conn, address = server.accept()
    print(f"{address[0]}:{address[1]} connected.")
    print("Receiving file...")
    receive_file(conn, "received-image.png")
    print("File received.")
print("Connection closed.")

Client:

# client.py
import os
import socket
import struct
def send_file(sck: socket.socket, filename):
    # Get the size of the outgoing file.
    filesize = os.path.getsize(filename)
    # First inform the server the amount of
    # bytes that will be sent.
    sck.sendall(struct.pack("<Q", filesize))
    # Send the file in 1024-bytes chunks.
    with open(filename, "rb") as f:
        while read_bytes := f.read(1024):
            sck.sendall(read_bytes)
with socket.create_connection(("localhost", 6190)) as conn:
    print("Connected to the server.")
    print("Sending file...")
    send_file(conn, "image.png")
    print("Sent.")
print("Connection closed.")

To test the code, make sure to modify the send_file() and receive_file() calls with the path of the file you want to send and the path where you want to receive it. In the previous code there is a file called image.png in the same folder where both Python files are located that is received as received-image.png. Once you've adjusted the file names, first run the server:

python server.py

Then run the client in another terminal:

python client.py

The server output will look like this:

Waiting for the client...
127.0.0.1:60331 connected.
Receiving file...
File received.
Connection closed.

And client output:

Connected to the server.
Sending file...
Sent.
Connection closed.