通过套接字发送包含文件的字典(python)

通过套接字发送包含文件的字典(python),python,file,sockets,dictionary,send,Python,File,Sockets,Dictionary,Send,是否可以通过套接字将包含文件(图像或文档)的dict作为值发送 我试过像贝娄这样的方法,但失败了 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") 这会导致json错误,myFile是无法序列化的字节数组 我

是否可以通过套接字将包含文件(图像或文档)的dict作为值发送

我试过像贝娄这样的方法,但失败了

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")
这会导致json错误,myFile是无法序列化的字节数组

我尝试使用base64编码将myFile转换为字符串,但没有成功

部分起作用的是将myFile转换成字符串,比如str(myFile)。json序列化程序工作正常,我通过套接字发送,dict正常,但myFile数据已损坏,因此我无法重新创建图片

那么,是否可以使用这种方法,或者如何通过套接字发送文件和数据,以便在另一端轻松解析

乐:

使用base64编码仍然不起作用,myFile仍然是“字节”格式和 json给出了以下错误:“字节”类型的对象不可json序列化

客户

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()

您应该能够做到:

data = base64.b64encode(myFile)
dataToSend = json.dumps({"id":"1283","filename":"cat.jpeg", "file":data})
然后通过套接字发送。当您在套接字的另一端接收数据时,只需执行以下操作:

jsonDict = json.loads(dataReceived)
data = base64.b64decode(jsonDict["file"])
更好的方法可能是只使用bson


您应该能够做到:

data = base64.b64encode(myFile)
dataToSend = json.dumps({"id":"1283","filename":"cat.jpeg", "file":data})
然后通过套接字发送。当您在套接字的另一端接收数据时,只需执行以下操作:

jsonDict = json.loads(dataReceived)
data = base64.b64decode(jsonDict["file"])
更好的方法可能是只使用bson


正如我在评论中所说的,将二进制数据打包成字符串格式(如JSON)是一种浪费——如果使用base64,则会将数据传输大小增加33%,这也使得JSON解码器很难正确解码JSON,因为它需要通过整个结构来提取索引

最好分别发送它们——JSON作为JSON,然后文件内容直接作为二进制。当然,您需要一种区分两者的方法,最简单的方法是在发送JSON数据时只在JSON数据前面加上其长度,以便服务器知道要读取多少字节才能获得JSON,然后读取其余的字节作为文件内容。这将使其成为一种非常简单的协议,包的形式如下:

[JSON LENGTH][JSON][FILE CONTENTS]
假设JSON永远不会超过4GB(如果是的话,你会遇到更大的问题,因为解析它将是一场噩梦),将固定4字节(32位)的
JSON长度作为无符号整数就足够了(如果你不希望JSON超过64KB,你甚至可以选择16位)因此,整个策略将在客户端起作用,如下所示:

  • 创建有效负载
  • 将其编码为JSON,然后使用UTF-8编码将其编码为
    bytes
  • 获取上述包的长度,并将其作为流的前4个字节发送
  • 发送JSON包
  • 读取并发送文件内容
  • 在服务器端,您执行相同的过程

  • 读取接收数据的前4个字节以获取JSON有效负载长度
  • 读取下一个字节数以匹配此长度
  • 使用UTF-8将它们解码为字符串,然后解码JSON以获得有效负载
  • 读取其余的流数据并将其存储到文件中
  • 或在代码中,客户端:

    import json
    import os
    import socket
    import struct
    
    BUFFER_SIZE = 4096  # a uniform buffer size to use for our transfers
    
    # pick up an absolute path from the script folder, not necessary tho
    file_path = os.path.abspath(os.path.join(os.path.dirname(__file__), "downloads", "cat.png"))
    
    # let's first prepare the payload to send over
    payload = {"id": 12, "filename": os.path.basename(file_path), "message": "So cute!"}
    # now JSON encode it and then turn it onto a bytes stream by encoding it as UTF-8
    json_data = json.dumps(payload).encode("utf-8")
    # then connect to the server and send everything
    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:  # create a socket
        print("Connecting...")
        s.connect(("127.0.0.1", 1234))  # connect to the server
        # first send the JSON payload length
        print("Sending `{filename}` with a message: {message}.".format(**payload))
        s.sendall(struct.pack(">I", len(json_data)))  # pack as BE 32-bit unsigned int
        # now send the JSON payload itself
        s.sendall(json_data)  # let Python deal with the buffer on its own for the JSON...
        # finally, open the file and 'stream' it to the socket
        with open(file_path, "rb") as f:
            chunk = f.read(BUFFER_SIZE)
            while chunk:
                s.send(chunk)
                chunk = f.read(BUFFER_SIZE)
        # alternatively, if you're using Python 3.5+ you can just use socket.sendfile() instead
        print("Sent.")
    
    和服务器:

    import json
    import os
    import socket
    import struct
    
    BUFFER_SIZE = 4096  # a uniform buffer size to use for our transfers
    
    target_path = os.path.abspath(os.path.join(os.path.dirname(__file__), "fileCache"))
    
    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
        s.bind(("127.0.0.1", 1234))  # bind to the 1234 port on localhost
        s.listen(0)  # allow only one connection so we don't have to deal with data separation
        while True:
            print("Waiting for a connection...")
            connection, address = s.accept()  # wait for and accept the incoming connection
            print("Connection from `{}` accepted.".format(address))
            # read the starting 32 bits and unpack them into an int to get the JSON length
            json_length = struct.unpack(">I", connection.recv(4))[0]
            # now read the JSON data of the given size and JSON decode it
            json_data = b""  # initiate an empty bytes structure
            while len(json_data) < json_length:
                chunk = connection.recv(min(BUFFER_SIZE, json_length - len(json_data)))
                if not chunk:  # no data, possibly broken connection/bad protocol
                    break  # just exit for now, you should deal with this case in production
                json_data += chunk
            payload = json.loads(json_data.decode("utf-8"))  # JSON decode the payload
            # now read the rest and store it into a file at the target path
            file_path = os.path.join(target_path, payload["filename"])
            with open(file_path, "wb") as f:  # open the target file for writing...
                chunk = connection.recv(BUFFER_SIZE)  # and stream the socket data to it...
                while chunk:
                    f.write(chunk)
                    chunk = connection.recv(BUFFER_SIZE)
            # finally, lets print out that we received the data
            print("Received `{filename}` with a message: {message}".format(**payload))
    
    导入json
    导入操作系统
    导入套接字
    导入结构
    缓冲区大小=4096#用于传输的统一缓冲区大小
    target\u path=os.path.abspath(os.path.join(os.path.dirname(\uuu file\uuu),“fileCache”))
    将socket.socket(socket.AF_INET,socket.SOCK_STREAM)作为s:
    s、 绑定(((“127.0.0.1”,1234))#绑定到本地主机上的1234端口
    s、 侦听(0)#只允许一个连接,因此我们不必处理数据分离问题
    尽管如此:
    打印(“正在等待连接…”)
    连接,地址=s.accept()#等待并接受传入连接
    打印(“已接受来自`{}`的连接。”。格式(地址))
    #读取起始的32位,并将其解压为int以获得JSON长度
    json_length=struct.unpack(“>I”,connection.recv(4))[0]
    #现在读取给定大小的JSON数据并对其进行JSON解码
    json_data=b“”#启动一个空字节结构
    而len(json_数据)
    注意:请记住,这是Python3.x代码-对于Python2.x,您必须自己处理上下文管理,而不是使用
    with…
    块来打开/关闭套接字


    这就是全部。当然,在实际环境中,您需要处理断开连接、多个客户端等问题,但这是基本过程。

    正如我在评论中所说,将二进制数据打包为字符串格式(如JSON)这是浪费—如果使用base64,则会将数据传输大小增加33%,这也会使JSON解码器很难正确解码JSON,因为它需要流式传输整个结构以提取索引

    最好分别发送它们——JSON作为JSON,然后文件内容直接作为二进制。当然,您需要一种区分两者的方法,最简单的方法就是在JSON数据前面加上它的l
    base64_bytes = b64encode(l)
    myFile = base64_bytes.decode("utf-8")
    
    import os
    from base64 import b64encode
    import json
    import socket
    
    currentPath = os.path.dirname(os.path.abspath(__file__)) + "\\downloads\\"
    
    with open(currentPath + "cat.png", "rb") as f:
        l = f.read()
    
    base64_bytes = b64encode(l)
    myFile = base64_bytes.decode("utf-8")
    
    data = {"id": "12", "filename": "cat.png", "message": "So cute!", "file": myFile}
    
    dataToSend = json.dumps(data).encode("utf-8")
    
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect(("127.0.0.1", 1234))
    s.sendall(dataToSend)
    s.close()
    
    import socket
    import json
    import os
    import sys
    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(4096)
    
        if sys.getsizeof(dataReceived) > 17:
            tempData = tempData + dataReceived
        else:
            data = json.loads(tempData.decode("utf-8"))
            break
    
    myFile = base64.b64decode(data["file"])
    
    with open(currentPath + data["filename"], "wb") as f:
        f.write(myFile)
        f.close()