'How to send zip file using Python sockets?

This is my server:

//server.py
import sys                                           
import socket                                                                                             
HOST = sys.argv[1] if len(sys.argv) > 1 else '0.0.0.0'                                                    
PORT = int(sys.argv[2] if len(sys.argv) > 2 else 5555)                                                    
SIZE = 1024                                          
FORMAT = "utf-8"                                     
s = socket.socket()                                  
s.setsockopt(socket.SOL_SOCKET, 
socket.SO_REUSEADDR, 1)                                                   
s.bind((HOST, PORT))                                                                                      
s.listen(1)                                          
client = s.accept()
while True:                                           
 print(f"\033[33m[*] Listening as {HOST}:{PORT}\033[m")                                                    
 print(f"\033[32m[!] Client connected {client[1]}\033[m")
 client[0].send('copy trash'.encode())                
 filename = client[0].recv(SIZE).decode(FORMAT)
 print(f"[RECV] Receiving the filename.")
 file = open(filename, "w")
 client[0].send("Filename received.".encode(FORMAT))
 data = client[0].recv(SIZE).decode(FORMAT)
 print(f"[RECV] Receiving the file data.")
 file.write(data)
 client[0].send("File data received".encode(FORMAT))
s.close()

This is my client:

//client.py
import sys                                           
import socket                                        
import subprocess                                    
import tqdm
import shutil
                                                 
HOST = sys.argv[1] if len(sys.argv) > 1 else '0.0.0.0'                                                    
PORT = int(sys.argv[2] if len(sys.argv) > 2 else 5555)                                                    
FORMAT = "utf-8"                                     
SIZE = 1024                                          
s = socket.socket()                                  
s.connect((HOST, PORT))                              
msg = s.recv(1024).decode()                          
print('[*] server:', msg)                            
if(msg.lower() == "copy trash"):                         
 shutil.make_archive("/root/trashcopy", 'zip', "/root/.local/share/Trash")                                 
 file = open("/root/trashcopy.zip", "r")     
 data = file.read() 
 s.send("trashcopy.zip".encode(FORMAT))           
 msg = s.recv(SIZE).decode(FORMAT)                    
 print(f"[SERVER]: {msg}")                            
 s.send(data.encode(FORMAT))                          
 msg = s.recv(SIZE).decode(FORMAT)
 print(f"[SERVER]: {msg}")

What am I trying to do?

I'm trying to make a zip file with files from the recycle bin and send it to the server, however, there's a problem with encoding and an error is thrown at this line:

s.send(data.encode(FORMAT))   

This is the error:

 UnicodeDecodeError: 'utf-8' codec can't decode byte 0x95 in position 124: invalid start byte

How can I fix this? When it's a .txt file, for example, I can send the file without problems.

It's a decoding problem, I've tried to decode in other formats besides utf-8 but it didn't work.



Solution 1:[1]

There were several issues with your code. For example, when reading the zip (binary) file, you should read it as bytes, not as string, and send it as bytes. Also, your server needs to know in advance the expected file size to be able to read the file in chunks. You can do that using byte ordering. Below is a working example. Credits go to this for the data receiving approach, which was slightly modified to read the data in chunks, as your code requires.

EDIT: The server has been modified to support reconnection from the client. Thus, client = s.accept() has been moved to inside the while loop and client's connection gets closed after the file has been received.

server.py

import sys                                           
import socket

HOST = sys.argv[1] if len(sys.argv) > 1 else '0.0.0.0'                                                    
PORT = int(sys.argv[2] if len(sys.argv) > 2 else 5555)                                                    
SIZE = 1024                                          
BYTEORDER_LENGTH = 8
FORMAT = "utf-8"
s = socket.socket()                                  
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)                                                   
s.bind((HOST, PORT))                                                                                      
s.listen(1)                                          

while True:
    client = s.accept()                                          
    print(f"\033[33m[*] Listening as {HOST}:{PORT}\033[m")                                                    
    print(f"\033[32m[!] Client connected {client[1]}\033[m")

    print(f"Sending 'copy trash' msg")
    client[0].send('copy trash'.encode())

    print(f"[RECV] Receiving the file size")
    file_size_in_bytes = client[0].recv(BYTEORDER_LENGTH)
    file_size= int.from_bytes(file_size_in_bytes, 'big')
    print("File size received:", file_size, " bytes")
    client[0].send("File size received.".encode(FORMAT))

    print(f"[RECV] Receiving the filename.")
    filename = client[0].recv(SIZE).decode(FORMAT)
    print(f"[RECV]Filename received:", filename)
    client[0].send("Filename received.".encode(FORMAT))

    print(f"[RECV] Receiving the file data.")
    # Until we've received the expected amount of data, keep receiving
    packet = b""  # Use bytes, not str, to accumulate
    while len(packet) < file_size:
        if(file_size - len(packet)) > SIZE:  # if remaining bytes are more than the defined chunk size
            buffer = client[0].recv(SIZE)  # read SIZE bytes
        else:
            buffer = client[0].recv(file_size - len(packet))  # read remaining number of bytes

        if not buffer:
            raise Exception("Incomplete file received")
        packet += buffer
    with open(filename, 'wb') as f:
        f.write(packet)
        
    print(f"[RECV] File data received.")
    client[0].send("File data received".encode(FORMAT))
    client[0].close()
s.close()

client.py

import sys                                           
import socket                                        
import subprocess                                    
import shutil
import os

HOST = sys.argv[1] if len(sys.argv) > 1 else '127.0.0.1'                                                    
PORT = int(sys.argv[2] if len(sys.argv) > 2 else 5555)                                                    
FORMAT = "utf-8"                                     
SIZE = 1024
BYTEORDER_LENGTH = 8                                         
s = socket.socket()                                  
s.connect((HOST, PORT))  
                            
msg = s.recv(SIZE).decode()                          
print('[*] server:', msg)     

if(msg.lower() == "copy trash"):                         
    #shutil.make_archive("/root/trashcopy", 'zip', "/root/.local/share/Trash")                                 
    shutil.make_archive("_trashcopy", 'zip', "Trash")
    
    file_size = os.path.getsize('_trashcopy.zip')
    print("File Size is :", file_size, "bytes")
    file_size_in_bytes = file_size.to_bytes(BYTEORDER_LENGTH, 'big')
    
    print("Sending the file size")
    s.send(file_size_in_bytes)
    msg = s.recv(SIZE).decode(FORMAT)                    
    print(f"[SERVER]: {msg}")
    
    print("Sending the file name")
    s.send("trashcopy.zip".encode(FORMAT))           
    msg = s.recv(SIZE).decode(FORMAT)                    
    print(f"[SERVER]: {msg}")  
    
    print("Sending the file data")    
    with open ('_trashcopy.zip','rb') as f1:
        s.send(f1.read())  
    msg = s.recv(SIZE).decode(FORMAT)
    print(f"[SERVER]: {msg}")
s.close()

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1