使用附加的二进制数据文件和 json 格式的元数据创建 HTTP 获取响应

问题描述

我使用 BaseHTTPServer.BaseHTTPRequestHandler 来实现我的服务器。

目前我只用二进制数据文件获取请求。

self.send_response(200)
self.send_header("Content-Type",'application/octet-stream')
self.send_header("Content-disposition",'attachment; filename="{}"'.format(os.path.basename(FILEPATH)))
fs = os.fstat(f.fileno())
self.send_header("Content-Length",str(fs.st_size))
self.end_headers()

现在要求添加一个部分,其中包括一些简短的 json 格式的配置数据(即 {'status': 'ok','type': 'keepalive'}),我宁愿在由唯一 http 标头或 http 正文分隔的同一响应上传递此信息。

这样做的最佳方法是什么?我想知道如何扩展我的代码支持这一点。

谢谢

解决方法

有很多方法可以做到这一点,我认为最好的选择将取决于您的接收方最容易理解的内容。

最直接的解释是使用内容类型 multipart/mixed https://www.w3.org/Protocols/rfc1341/7_2_Multipart.html 但您可能必须在接收端编写自己的解析。我不知道这是否完全符合规范,但它传达了这个想法:

from http.server import BaseHTTPRequestHandler
import http.server
import socketserver
import string
import random
import io

PORT = 8000

class ResponsePart:
    def __init__(self,content,content_type):
        self.content = content.encode('utf-8')
        self.content_type = content_type.encode('utf-8')

class Mine(http.server.BaseHTTPRequestHandler):
    def get_separator(self,parts):
        while True:
            boundary = []
            for i in range(32):
                boundary.append(random.choice(string.digits + string.ascii_letters))
            boundary = ''.join(boundary).encode('ascii')
            for part in parts:
                if boundary in part:
                    break
            else:
                return boundary

    def do_GET(self):
        responses = [
                ResponsePart('abc123','Content-type: application/octet-stream'),ResponsePart('{"a":"b"}','Content-type: application/json'),]
        boundary = self.get_separator([r.content for r in responses])
        self.send_response(200)
        self.send_header("Content-Type",'multipart/mixed; boundary=' + boundary.decode('ascii'))
        self.end_headers()

        for piece in responses:
            self.wfile.write(b'--')
            self.wfile.write(boundary)
            self.wfile.write(b'\r\n')
            self.wfile.write(piece.content_type)
            self.wfile.write(b'\r\n')
            self.wfile.write(piece.content)
            self.wfile.write(b'\r\n')

Handler = Mine

with socketserver.TCPServer(("",PORT),Handler) as httpd:
    httpd.serve_forever()

除此之外,我可能会使用 JSON 或其他方法来执行此操作,以便您返回单一一致的内容类型:

from http.server import BaseHTTPRequestHandler
import http.server
import socketserver
import string
import random
import io
import json

PORT = 8000

class Mine(http.server.BaseHTTPRequestHandler):
    def do_GET(self):
        response = {
                'filedata': 'abc123','status': {"a":"b"},}
        output_data = json.dumps(response).encode('utf-8')

        self.send_response(200)
        self.send_header("Content-Type",'application/octet-stream')
        self.send_header("Content-Length",str(len(output_data)))
        self.end_headers()

        self.wfile.write(output_data)

Handler = Mine

with socketserver.TCPServer(("",Handler) as httpd:
    httpd.serve_forever()

这在接收端处理起来要容易得多,一个 json 解码就完成了。