13

我正在尝试使用 Python 中的请求发送多部分/相关消息。该脚本看起来很简单,除了 requests 似乎只允许发送 multipart/form-data 消息,尽管他们的文档没有明确说明这种或另一种方式。

我的用例是发送带有附件的肥皂。我可以提供一个字典,其中包含两个文件,其内容是测试肥皂消息,以及我要发送的测试文档。第一个包含带有所有说明的肥皂消息,第二个是实际文档。

但是,如果我没有指定 headers 值,则在使用 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

有没有办法使用请求来完成这项工作?或者还有其他我应该看的工具吗?

4

2 回答 2

25

您必须自己创建 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)

我假设 XML 文件使用 UTF-8,您可能还想为document条目设置一个字符集。

requests只知道如何创建multipart/form-data帖子正文;不multipart/related 常用。

于 2013-04-02T12:03:09.237 回答
6

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

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

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

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

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

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']
于 2017-12-06T20:25:09.883 回答