【问题标题】:parse multipart/form-data, received from requests post解析多部分/表单数据,从请求后接收
【发布时间】:2016-01-26 22:37:48
【问题描述】:

我正在使用请求库编写 Web 服务客户端。我在包含文件和文本 json 的 multipart/form-data 中获取数据。我不知道如何解析它。是否有合适的库来解析 python 中的 multipart/form-data 格式,还是我应该自己编写解析器?

我的代码:

data = {
  "prototypeModel" :('prototypeModel', open(prototypeModel, 'rb'), 'application/octet-stream', {'Expires': '0'}),
  "mfcc_1" : ('mfcc', open(mfcc_1, 'rb'), 'application/octet-stream', {'Expires': '0'}),
  "mfcc_2" : ('mfcc', open(mfcc_2, 'rb'), 'application/octet-stream', {'Expires': '0'}),
  "mfcc_3" : ('mfcc', open(mfcc_3, 'rb'), 'application/octet-stream', {'Expires': '0'}),
}

print( '---------------------- start enroll ----------------------')
testEnrollResponse = requests.post(server+sessionID, files = data, json = declaredParameters)

b'\r\n--c00750d1-8ce4-4d29-8390-b50bf02a92cc\r\n内容配置: 表格数据; name="playbackHash"\r\n内容类型: 应用程序/八位字节流\r\n\r\n\x16\x00\x00\x00\x00\x00\x00\x00serialization::archive\n\x00\x04\x08\x04 …… x00\x00R\x94\x9bp\x8c\x00\r\n--c00750d1-8ce4-4d29-8390-b50bf02a92cc\r\n内容配置: 表格数据; name="usersMFCC"\r\n内容类型: 应用程序/八位字节流\r\n\r\n\x16\x00\x00\x00\x00\x00\x00\x00serialization::archive\n\x00\x04\x08\x04\x08\x01\x00\x00 \x00\x00\x00\x00\x00\x00\xf8\x16\x00\x00\x00\x00\x00\x00u\xbd\xb4/\xda1\xea\xbf\x0f\xed\xa2 x80\xd9\x95Yxn\xd0?\r\n--c00750d1-8ce4-4d29-8390-b50bf02a92cc\r\n内容-处置: 表格数据; name="scoreAndStatus"\r\nContent-Type: application/json; charset=utf-8\r\n\r\n{"lexLikelihood":1.544479046897232,"overallScore":-nan,"playbackLikelihood":-inf,"status":{"errorCode":0,"errorMessage":" "}}\r\n--c00750d1-8ce4-4d29-8390-b50bf02a92cc--\r\n'

我用“.....”替换了更多的二进制数据

【问题讨论】:

  • 向我们展示您收到的回复。

标签: python web-services client python-requests


【解决方案1】:

如果您收到 multipart/form-data 响应,您可以使用 requests-toolbelt 库解析它,如下所示:

$ pip install requests-toolbelt

安装后

from requests_toolbelt.multipart import decoder

testEnrollResponse = requests.post(...)
multipart_data = decoder.MultipartDecoder.from_response(testEnrollResponse)

for part in multipart_data.parts:
    print(part.content)  # Alternatively, part.text if you want unicode
    print(part.headers)

【讨论】:

    【解决方案2】:

    Flask 的代码示例,使用 https://github.com/defnull/multipart

    import multipart as mp
    from multipart import tob
    
    try:
        from io import BytesIO
    except ImportError:
        from StringIO import StringIO as BytesIO
    
    @app.route('/', methods=["GET","POST"])
    def index():
            ...
            elif flask.request.method == "POST":
                    data = flask.request.data
                    s = data.split("\r")[0][2:]
                    p = mp.MultipartParser(BytesIO(tob(data)),s)
                    blob = p.parts()[0].value
                    f = open("file.bin","wb")
                    f.write(blob.encode("latin-1"))
                    f.close()
    

    【讨论】:

    • 它是关于请求客户端而不是烧瓶服务器
    • 是的,您可以将我的代码改编为 Web 服务客户端。我的代码没有使用 python-requests 模块。它正在工作。
    • 但这与问题无关。
    • 它有正确的信息,但它没有以任何方式回答问题。
    • 这有有用的信息,事实上,当我试图为服务器端找到类似的东西时,这篇文章就出现了。即使问题是双向的,它表示接收请求,可能来自客户端。
    【解决方案3】:

    下面是解析多部分数据的working example。您可以在交互式 python 提示符下尝试一下。

    import email
    
    msg = email.message_from_string('''\
    MIME-Version: 1.0
    Content-Type: multipart/mixed; boundary="    XXXX"
    
    --    XXXX
    Content-Type: text/plain
    
    
    --    XXXX
    Content-Type: text/plain
    
    --    XXXX--
    ''')
    
    msg.is_multipart()
    

    一旦您知道它在您的系统上运行,您就可以根据 POST 数据构建您自己的电子邮件消息并以相同的方式对其进行解析。如果您将原始post body 作为字符串,则可以在请求标头中找到其余必要信息。为了清楚起见,我在此处添加了缩进,您不应该在块字符串中有多余的缩进。

        epost_data = '''\
    MIME-Version: 1.0
    Content-Type: %s
    
    %s''' % (self.headers['content-type'], post_data)
    
        msg = email.message_from_string(post_data)
    
        if msg.is_multipart():
            for part in msg.get_payload():
                name = part.get_param('name', header='content-disposition')
                filename = part.get_param('filename', header='content-disposition')
                # print 'name %s' % name # "always" there
                # print 'filename %s' % filename # only there for files...
                payload = part.get_payload(decode=True)
                print payload[:100] # output first 100 characters
    

    第一个%s 将替换为内容类型,第二个将替换为post_data。然后,您可以将有效负载写入文件等。

    请小心考虑保存文件的安全隐患。您可能无法信任发布的文件名,它可能以 ../../filename.sh 开头,例如在某些 Web 服务器上,因此如果您尝试编写 /my-folder/../../filename.sh,攻击者可能会将恶意文件放置在您所在位置之外试图存储文件。还建议在信任文件本身之前对文件作为允许的类型进行强验证。您不想让攻击者覆盖您系统上的任何文件。

    【讨论】:

      猜你喜欢
      • 2019-06-01
      • 2014-09-21
      • 1970-01-01
      • 2010-11-07
      • 1970-01-01
      • 2019-10-13
      • 2010-09-11
      • 2012-11-27
      • 2016-12-17
      相关资源
      最近更新 更多