在python2.7中,我使用requests
与REST端点进行通信。我可以上传单个JSON和xml对象。
为了加快速度,我想使用multipart上传多个json对象。
我有一个curl命令,显示它是如何完成的,这是有效的。 我需要在python请求POST命令中翻译它。
工作卷曲POST:
curl --anyauth --user admin:admin -X POST --data-binary \@sample-body \
-i -H "Content-type: multipart/mixed; boundary=BOUNDARY" \
"http://localhost:8058/v1/resources/sight-ingest?rs:transform=aireco-transform&rs:title=file1.xml&rs:title=file2.xml&rs:title=file3.xml"
注意事项: 我需要发送包含'title'参数列表的自定义参数列表,不能通过传递dict来做到这一点?但我们可以解决这个问题。
我的python路径:
import requests
files = {'file1': ('foo.txt', 'foo\ncontents\n','text/plain'),
'file2': ('bar.txt', 'bar contents', 'text/plain'),
'file3': ('baz.txt', 'baz contents', 'text/plain')}
headers = {'Content-Type': 'multipart/mixed','Content-Disposition': 'attachment','boundary': 'GRENS'}
params={'title':'file1','title':'file2','title':'file2'}
r = requests.Request('POST', 'http://example.com', files=files , headers=headers, params=params)
print r.prepare().url
print r.prepare().headers
print r.prepare().body
给我:
http://example.com/?title=file2
{'boundary': 'GRENS', 'Content-Type': 'multipart/mixed', 'Content-Length': '471', 'Content-Disposition': 'attachment'}
--7f18a6c1b09f42009228f600b0af35fd
Content-Disposition: form-data; name="file3"; filename="baz.txt"
Content-Type: text/plain
baz contents
--7f18a6c1b09f42009228f600b0af35fd
Content-Disposition: form-data; name="file2"; filename="bar.txt"
Content-Type: text/plain
bar contents
--7f18a6c1b09f42009228f600b0af35fd
Content-Disposition: form-data; name="file1"; filename="foo.txt"
Content-Type: text/plain
foo
contents
--7f18a6c1b09f42009228f600b0af35fd--
答案 0 :(得分:2)
不要使用字典,请使用(key, value)
元组列表作为查询参数:
params = [('title', 'file1'), ('title', 'file2'), ('title', 'file3')]
否则你只会得到一把钥匙。
您应该不设置Content-Type
标头;当您使用requests
参数时,files
会为您正确设置;这样,也将包括正确的边界。你不应该自己直接设置边界,真的:
params = [('title', 'file1'), ('title', 'file2'), ('title', 'file3')]
r = requests.post('http://example.com',
files=files, headers=headers, params=params)
您可以通过向每个文件元组添加第4个元素来设置标题每个文件部分,以获取额外的标头,但在您的情况下,您不应尝试设置Content-Disposition
标头自己;无论如何它都会被覆盖。
反省准备好的请求对象然后给你:
>>> import requests
>>> from pprint import pprint
>>> files = {'file1': ('foo.txt', 'foo\ncontents\n','text/plain'),
... 'file2': ('bar.txt', 'bar contents', 'text/plain'),
... 'file3': ('baz.txt', 'baz contents', 'text/plain')}
>>> headers = {'Content-Disposition': 'attachment'}
>>> params = [('title', 'file1'), ('title', 'file2'), ('title', 'file3')]
>>> r = requests.Request('POST', 'http://example.com',
... files=files, headers=headers, params=params)
>>> prepared = r.prepare()
>>> prepared.url
'http://example.com/?title=file1&title=file2&title=file3'
>>> pprint(dict(prepared.headers))
{'Content-Disposition': 'attachment',
'Content-Length': '471',
'Content-Type': 'multipart/form-data; boundary=7312ccd96db94419bf1d97f2c54bbad1'}
>>> print prepared.body
--7312ccd96db94419bf1d97f2c54bbad1
Content-Disposition: form-data; name="file3"; filename="baz.txt"
Content-Type: text/plain
baz contents
--7312ccd96db94419bf1d97f2c54bbad1
Content-Disposition: form-data; name="file2"; filename="bar.txt"
Content-Type: text/plain
bar contents
--7312ccd96db94419bf1d97f2c54bbad1
Content-Disposition: form-data; name="file1"; filename="foo.txt"
Content-Type: text/plain
foo
contents
--7312ccd96db94419bf1d97f2c54bbad1--
如果您绝对必须multipart/mixed
而不是multipart/form-data
,那么您必须自己构建POST正文并设置标题。包含的urllib3
tools应该可以为您执行此操作:
from requests.packages.urllib3.fields import RequestField
from requests.packages.urllib3.filepost import encode_multipart_formdata
fields = []
for name, (filename, contents, mimetype) in files.items():
rf = RequestField(name=name, data=contents,
filename=filename)
rf.make_multipart(content_disposition='attachment', content_type=mimetype)
fields.append(rf)
post_body, content_type = encode_multipart_formdata(fields)
content_type = ''.join(('multipart/mixed',) + content_type.partition(';')[1:])
headers = {'Content-Type': content_type}
requests.post('http://example.com', data=post_body, headers=headers, params=params)
或者您可以使用email
package执行相同操作:
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
body = MIMEMultipart()
for name, (filename, contents, mimetype) in files.items():
part = MIMEText(contents, _subtype=mimetype.partition('/')[-1], _charset='utf8')
part.add_header('Content-Disposition', 'attachment', filename=filename)
body.attach(part)
post_body = body.as_string().partition('\n\n')[-1]
content_type = body['content-type']
headers = {'Content-Type': content_type}
requests.post('http://example.com', data=post_body, headers=headers, params=params)
但请注意,此方法需要您设置一个字符集(我假设JSON和XML为UTF-8),并且它很可能使用Base64编码作为内容:
>>> body = MIMEMultipart()
>>> for name, (filename, contents, mimetype) in files.items():
... part = MIMEText(contents, _subtype=mimetype.partition('/')[-1], _charset='utf8')
... part.add_header('Content-Disposition', 'attachment', filename=filename)
... body.attach(part)
...
>>> post_body = body.as_string().partition('\n\n')[-1]
>>> content_type = body['content-type']
>>> print post_body
--===============1364782689914852112==
MIME-Version: 1.0
Content-Type: text/plain; charset="utf-8"
Content-Transfer-Encoding: base64
Content-Disposition: attachment; filename="baz.txt"
YmF6IGNvbnRlbnRz
--===============1364782689914852112==
MIME-Version: 1.0
Content-Type: text/plain; charset="utf-8"
Content-Transfer-Encoding: base64
Content-Disposition: attachment; filename="bar.txt"
YmFyIGNvbnRlbnRz
--===============1364782689914852112==
MIME-Version: 1.0
Content-Type: text/plain; charset="utf-8"
Content-Transfer-Encoding: base64
Content-Disposition: attachment; filename="foo.txt"
Zm9vCmNvbnRlbnRzCg==
--===============1364782689914852112==--
>>> print content_type
multipart/mixed; boundary="===============1364782689914852112=="