如何在python中发送带有请求的"multipart/related"?

Zac*_*ick 12 python mime multipart python-requests

我正在尝试使用Python中的请求发送多部分/相关消息.该脚本看起来很简单,只是请求似乎只允许发送多部分/表单数据消息,尽管他们的文档没有明确说明这种或那种方式.

我的用例是发送带有附件的肥皂.我可以提供一个字典,其中包含两个文件,其内容是测试soap消息,以及我正在尝试发送的测试文档.第一个包含带有所有指令的soap消息,第二个是实际文档.

但是,如果我没有指定标头值,则在使用files选项时,请求似乎只使用multipart/form-data.但是,如果我在尝试指定不同的多部分类型时指定标题,则请求似乎不会添加到mime边界信息中.

url = 'http://10.10.10.90:8020/foo'
headers = {'content-type': 'multipart/related'}
files = {'submission': open('submission_set.xml', 'rb'), 'document': open('document.txt', 'rb')}
response = requests.post(url, data=data, headers=headers)
print response.text
Run Code Online (Sandbox Code Playgroud)

有没有办法使用请求完成此操作?还是我应该看到另一种工具?

Mar*_*ers 23

您必须自己创建MIME编码.您可以使用该email.mime包:

import requests
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText

related = MIMEMultipart('related')

submission = MIMEText('text', 'xml', 'utf8')
submission.set_payload(open('submission_set.xml', 'rb').read())
related.attach(submission)

document = MIMEText('text', 'plain')
document.set_payload(open('document.txt', 'rb').read())
related.attach(document)

body = related.as_string().split('\n\n', 1)[1]
headers = dict(related.items())

r = requests.post(url, data=body, headers=headers)
Run Code Online (Sandbox Code Playgroud)

我假设XML文件使用UTF-8,您可能也想为该document条目设置字符集.

requests只知道如何创建multipart/form-data帖子体; 这multipart/related 是不常用的.

  • 该示例存在一个问题 - `headers[Content-Type]` 实际上没有 `boundary` 参数。您应该在 `items()` 之前调用 `as_string()` 以让它生成它,或者在 `MIMEMultipart` 构造函数中提供 `boundary`。在 python 3.5.0 上测试。 (2认同)

end*_*672 5

我正在使用requestsGoogle Drive API“Multipart”上传。

email.mime解决方案不适用于 Google 的 API,因此我深入研究了requests源代码以了解它是如何实现multipart/form-data主体的。

requests使用urllib3.filepost.encode_multipart_formdata()助手,它可以被包装以提供multipart/related

from urllib3.filepost import encode_multipart_formdata, choose_boundary

def encode_multipart_related(fields, boundary=None):
    if boundary is None:
        boundary = choose_boundary()

    body, _ = encode_multipart_formdata(fields, boundary)
    content_type = str('multipart/related; boundary=%s' % boundary)

    return body, content_type
Run Code Online (Sandbox Code Playgroud)

现在我们可以使用encode_multipart_related()来创建一个(body, content_type)符合谷歌要求的元组:

import json
from urllib3.fields import RequestField

def encode_media_related(metadata, media, media_content_type):
    rf1 = RequestField(
        name='placeholder',
        data=json.dumps(metadata),
        headers={'Content-Type': 'application/json; charset=UTF-8'},
    )
    rf2 = RequestField(
        name='placeholder2',
        data=media,
        headers={'Content-Type': media_content_type},
    )
    return encode_multipart_related([rf1, rf2])
Run Code Online (Sandbox Code Playgroud)

这是一个完整的示例,它使用我们encode_media_related()google_auth库将 hello world 文件上传到 Google Drive 。

from google.oauth2 import service_account
import google.auth.transport.requests

credentials = service_account.Credentials.from_service_account_file(
    PATH_TO_SERVICE_FILE,
    scopes=['https://www.googleapis.com/auth/drive.file'],
)
session = google.auth.transport.requests.AuthorizedSession(credentials)

metadata = {
    'mimeType': 'application/vnd.google-apps.document',
    'name': 'Test Upload',
}
body, content_type = encode_media_related(
    metadata,
    '<html><body><p>Hello World!</body></html>',
    'text/html; charset=UTF-8',
)
resp = session.post(
    'https://www.googleapis.com/upload/drive/v3/files',
    data=body,
    params={'uploadType': 'multipart'},
    headers={'Content-Type': content_type},
)

print 'Uploaded to file with id: %s' % resp.json()['id']
Run Code Online (Sandbox Code Playgroud)