我正在尝试使用Python的Requests库将请求发布到Amazon S3端点。该请求属于multipart / form-data类型,因为它包含实际文件的POST。
我正在使用的API指定的一个要求是file
参数必须最后发布。由于Requests使用字典来POST多部分/表单数据,并且由于字典不遵循口述顺序,因此我将其转换为名为payload
的OrderedDict。在POST之前它看起来像这样:
{'content-type': 'text/plain',
'success_action_redirect': 'https://ian.test.instructure.com/api/v1/files/30652543/create_success?uuid=<opaque_string>',
'Signature': '<opaque_string>',
'Filename': '',
'acl': 'private',
'Policy': '<opaque_string>',
'key': 'account_95298/attachments/30652543/log.txt',
'AWSAccessKeyId': '<opaque_string>',
'file': '@log.txt'}
这就是我发布它的方式:
r = requests.post("https://instructure-uploads.s3.amazonaws.com/", files = payload)
响应是500错误,所以我真的不确定这里的问题是什么。我只是猜测它与我在请求中使用OrderedDict有关 - 我找不到任何建议Requests支持或不支持OrderedDicts的文档。它可能是完全不同的东西。
是否有其他事情会导致请求失败?如果需要,我可以提供更多细节。
好的,根据Martijn Pieters之前的评论进行更新:
我改变了我引用log.txt文件的方式,将它添加到已创建的upload_data
字典中,如下所示:
upload_data['file'] = open("log.txt")
打印生成的字典我得到了这个:
{'AWSAccessKeyId': '<opaque_string>',
'key': '<opaque_string>',
'Policy': '<opaque_string>',
'content-type': 'text/plain',
'success_action_redirect': 'https://ian.test.instructure.com/api/v1/files/30652688/create_success?uuid=<opaque_string>',
'Signature': '<opaque_string>',
'acl': 'private',
'Filename': '',
'file': <_io.TextIOWrapper name='log.txt' mode='r' encoding='UTF-8'>}
file
键的值是否正确?
当我将它发布到RequestBin时,我得到了这个,看起来非常类似于Martin的例子:
POST /1j92n011 HTTP/1.1
User-Agent: python-requests/1.1.0 CPython/3.3.0 Darwin/12.2.0
Host: requestb.in
Content-Type: multipart/form-data; boundary=e8c3c3c5bb9440d1ba0a5fe11956e28d
Content-Length: 2182
Connection: close
Accept-Encoding: identity, gzip, deflate, compress
Accept: */*
--e8c3c3c5bb9440d1ba0a5fe11956e28d
Content-Disposition: form-data; name="AWSAccessKeyId"; filename="AWSAccessKeyId"
Content-Type: application/octet-stream
<opaque_string>
--e8c3c3c5bb9440d1ba0a5fe11956e28d
Content-Disposition: form-data; name="key"; filename="key"
Content-Type: application/octet-stream
<opaque_string>
--e8c3c3c5bb9440d1ba0a5fe11956e28d
Content-Disposition: form-data; name="Policy"; filename="Policy"
Content-Type: application/octet-stream
<opaque_string>
--e8c3c3c5bb9440d1ba0a5fe11956e28d
Content-Disposition: form-data; name="content-type"; filename="content-type"
Content-Type: application/octet-stream
text/plain
--e8c3c3c5bb9440d1ba0a5fe11956e28d
Content-Disposition: form-data; name="success_action_redirect"; filename="success_action_redirect"
Content-Type: application/octet-stream
https://ian.test.instructure.com/api/v1/files/30652688/create_success?uuid=<opaque_string>
--e8c3c3c5bb9440d1ba0a5fe11956e28d
Content-Disposition: form-data; name="Signature"; filename="Signature"
Content-Type: application/octet-stream
<opaque_string>
--e8c3c3c5bb9440d1ba0a5fe11956e28d
Content-Disposition: form-data; name="acl"; filename="acl"
Content-Type: application/octet-stream
private
--e8c3c3c5bb9440d1ba0a5fe11956e28d
Content-Disposition: form-data; name="Filename"; filename="Filename"
Content-Type: application/octet-stream
--e8c3c3c5bb9440d1ba0a5fe11956e28d
Content-Disposition: form-data; name="file"; filename="log.txt"
Content-Type: text/plain
This is my awesome test file.
--e8c3c3c5bb9440d1ba0a5fe11956e28d--
但是,当我尝试将其发送到https://instructure-uploads.s3.amazonaws.com/时,我仍然会收到500。我尝试将打开的文件对象添加到files
,然后通过data
在单独的dict中提交所有其他值,但这也不起作用。
答案 0 :(得分:3)
您需要将发送的内容拆分为传递给data
的OrderedDict,将其发送到files
。现在,AWS(正确地)将您的数据参数解释为FILES,而不是表格参数。它应该是这样的:
data = OrderedDict([
('AWSAccessKeyId', '<opaque_string>'),
('key', '<opaque_string>'),
('Policy', '<opaque_string>'),
('content-type', 'text/plain'),
('success_action_redirect', 'https://ian.test.instructure.com/api/v1/files/30652688/create_success?uuid=<opaque_string>'),
('Signature', '<opaque_string>'),
('acl', 'private'),
('Filename', ''),
])
files = OrderedDict([('file', open('log.txt'))])
requests.post(url, data=data, files=files)
答案 1 :(得分:0)
您可以传递dict
,或一系列双值元组。
并且OrderedDict
被简单地转换为这样的序列:
r = requests.post("https://instructure-uploads.s3.amazonaws.com/", files=payload.items())
但是,由于collections.OrderedDict()
类型是dict
的子类,因此调用items()
正好 requests
所做的内容,因此直接传入OrderedDict
实例也是Just Works。
因此,其他事情是错误的。您可以通过发布到http://httpbin/post
来验证发布的内容:
import pprint
pprint.pprint(requests.post("http://httpbin.org/post", files=payload.items()).json())
不幸的是,httpbin.org
不会保留排序。或者,您也可以在http://requestb.in/创建专用的HTTP邮箱;它会更详细地告诉你发生了什么。
使用requestb.in,并将'@log.txt'
替换为打开的文件对象,请求中的POST记录为:
POST /tlrsd2tl HTTP/1.1
User-Agent: python-requests/1.1.0 CPython/2.7.3 Darwin/11.4.2
Host: requestb.in
Content-Type: multipart/form-data; boundary=7b12bf345d0744b6b7e66c7890214311
Content-Length: 1601
Connection: close
Accept-Encoding: gzip, deflate, compress
Accept: */*
--7b12bf345d0744b6b7e66c7890214311
Content-Disposition: form-data; name="content-type"; filename="content-type"
Content-Type: application/octet-stream
text/plain
--7b12bf345d0744b6b7e66c7890214311
Content-Disposition: form-data; name="success_action_redirect"; filename="success_action_redirect"
Content-Type: application/octet-stream
https://ian.test.instructure.com/api/v1/files/30652543/create_success?uuid=<opaque_string>
--7b12bf345d0744b6b7e66c7890214311
Content-Disposition: form-data; name="Signature"; filename="Signature"
Content-Type: application/octet-stream
<opaque_string>
--7b12bf345d0744b6b7e66c7890214311
Content-Disposition: form-data; name="Filename"; filename="Filename"
Content-Type: application/octet-stream
--7b12bf345d0744b6b7e66c7890214311
Content-Disposition: form-data; name="acl"; filename="acl"
Content-Type: application/octet-stream
private
--7b12bf345d0744b6b7e66c7890214311
Content-Disposition: form-data; name="Policy"; filename="Policy"
Content-Type: application/octet-stream
<opaque_string>
--7b12bf345d0744b6b7e66c7890214311
Content-Disposition: form-data; name="key"; filename="key"
Content-Type: application/octet-stream
account_95298/attachments/30652543/log.txt
--7b12bf345d0744b6b7e66c7890214311
Content-Disposition: form-data; name="AWSAccessKeyId"; filename="AWSAccessKeyId"
Content-Type: application/octet-stream
<opaque_string>
--7b12bf345d0744b6b7e66c7890214311
Content-Disposition: form-data; name="file"; filename="log.txt"
Content-Type: text/plain
some
data
--7b12bf345d0744b6b7e66c7890214311--
显示正确保留了排序。
请注意,requests
不支持特定于Curl的@filename
语法;相反,传入一个打开的文件对象:
'file': open('log.txt', 'rb')
您可能还想将content-type
字段设置为使用标题大小写:'Content-Type': ..
。
如果您仍然收到500回复,请查看r.text
回复文字,了解亚马逊认为错误的内容。