Is it possible to send a dict that contains a file(image or document) as a value through a socket?
I tried something like bellow but i failed..
with open("cat.jpeg", "rb") as f:
myFile = f.read(2048)
data = {"id": "1283", "filename": "cat.jpeg", "file": myFile}
dataToSend = json.dumps(data).encode("utf-8")
This gives a json error, myFile being a byte array can't be serialized.
I tried coverting the myFile into a string using the base64 encode but it didn't worked.
What partially worked was casting myFile into a string, like str(myFile). The json serializer worked, i send it through the socket, the dict was ok but the myFile data was corrupted so i couldn't recreate the picture.
So is it possible using this approach or how should i send the file and the data through a socket to be easily parsed on the other side?
LE:
Still doesn't work using base64 encoding, myFile is still "bytes" format and json gives this error: TypeError: Object of type 'bytes' is not JSON serializable
Client
import os
import base64
import json
import socket
currentPath = os.path.dirname(os.path.abspath(__file__)) + "\\downloads\\"
with open(currentPath + "cat.png", "rb") as f:
l = f.read()
print(type(l)) #prints <class 'bytes'>
myFile = base64.b64encode(l)
print(type(myFile)) #prints <class 'bytes'>
data = {"id": "12", "filename": "cat.png", "message": "So cute!", "file": myFile}
dataToSend = json.dumps(data).encode("utf-8") #prints TypeError: Object of type 'bytes' is not JSON serializable
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(("127.0.0.1", 1234))
s.sendall(dataToSend)
s.close()
And the Server:
import socket
import json
import os
import sys
import time
import base64
currentPath = os.path.dirname(os.path.abspath(__file__)) + "\\fileCache\\"
tempData = bytearray()
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind(("127.0.0.1", 1234))
s.listen(5)
conn, addr = s.accept()
while True:
dataReceived = conn.recv(2048)
if sys.getsizeof(dataReceived) > 17:
tempData = tempData + dataReceived
else:
data = json.loads(tempData.decode("utf-8"))
break
time.sleep(1)
print(data)
myFile = base64.b64decode(data["file"])
with open(currentPath + data["filename"], "wb") as f:
f.write(myFile)
f.close()
You should be able to do:
and then send through the socket. When you receive the data on the other end of the socket, just do:
A better way might be to just use bson, https://github.com/py-bson/bson.
As I was saying in my comment, packing binary data into a string format (like JSON) is wasteful - if you use base64 you're increasing the data transfer size by 33% and it also makes it hard for the JSON decoder to properly decode the JSON as it needs to stream through the whole structure just to extract the indices.
It's much better to send them separately - JSON as JSON, and then the file contents straight as binary. Of course, you'll need a way to distinguish between the two and the easiest is to just preface the JSON data with its length when sending it so that the server knows how much bytes to read to obtain the JSON, and then read the rest as the file contents. This would make it a sort of a very simple protocol with packages formed as:
Assuming that the JSON will never be larger than 4GB (and if it is, you'll have much bigger problems as parsing it would be a nightmare) it's more than enough to have the
JSON LENGTH
of fixed 4 bytes (32 bits) as an unsigned integer (you can even go for 16-bit if you don't expect the JSON to go over 64KB) so the whole strategy would work on the client side as:bytes
using UTF-8 encodingAnd on the server side you do the same process
Or in code, client:
And the server:
NOTE: Keep in mind that this is Python 3.x code - for Python 2.x you'll have to deal with context management yourself instead of having the
with ...
block to open/close your sockets.And that's all there is to it. Of course, in a real setting you need to deal with disconnects, multiple clients, etc. But this is the underlying process.
Thanks everyone for help, i finally done it using base64. I found the answer here on stack overflow, i forgot the link to it but here it goes.
I had to encode and decode the file like this before using json.dumps.
Here is a working example:
Client:
Server: