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.