python-requests 似乎支持自定义集合作为要上传的文件的列表/映射。
这反过来应该允许我添加自定义标题字段,每个文件都在一个分段上传中上传。
我该怎么做呢?
python-requests 似乎支持自定义集合作为要上传的文件的列表/映射。
这反过来应该允许我添加自定义标题字段,每个文件都在一个分段上传中上传。
我该怎么做呢?
python-requests 似乎支持自定义集合作为要上传的文件的列表/映射。
这反过来应该允许我添加自定义标题字段,每个文件都在一个分段上传中上传。
没有。列表/映射中的每个文件必须是文件对象(或内容字符串)、文件名和文件对象(或内容)的 2 元组,或文件名、文件对象(或内容)和文件的 3 元组类型。其他任何事情都是非法的。
除非#1640被上游接受,在这种情况下,您所要做的就是使用文件名、文件对象(或内容)、文件类型和标题字典的 4 元组。
此时正确的做法可能是使用不同的库。例如,如果您urllib3
直接使用而不是通过request
包装器使用,它会使您想做的事情变得相当容易。您只需要处理使用urllib3
而不是requests
.
同时,您可以提出功能请求requests
,将来可能会添加更简单的方法。
但是有点令人沮丧的是,您需要的功能都在幕后,您就是无法使用它。
看起来干净利落地做事将是一场噩梦。但这都是非常简单的代码,我们可以很容易地破解它,所以让我们这样做。
看看requests.PreparedRequest.prepare_body
。它期望每个文件files
是文件名、内容或文件对象的元组,以及可选的内容类型。它基本上只是读取任何文件对象以将它们转换为内容,并将所有内容直接传递到urllib3.filepost.encode_multipart_formdata
. 因此,除非我们想替换此代码,否则我们将需要使用这些值之一将标头走私。让我们通过传递来做到这一点(filename, contents, (content_type, headers_dict))
。所以,requests
它本身是不变的。
urllib3.filepost.encode_multipart_formdata
那它叫什么?如您所见,如果您将文件的元组传递给它,它会调用一个名为 的函数iter_field_objects
,该函数最终会调用urllib3.fields.RequestField.from_tuples
每个文件。但是,如果您查看from_tuples
备用构造函数,它会说它用于处理构造 RequestField 对象的“旧式”方式,而普通构造器用于“新式”方式,实际上确实可以让您传入标头.
所以,我们需要做的就是 monkeypatch iter_field_objects
,用新的方式替换它的最后一行,我们应该完成了。咱们试试吧:
import requests
import requests.packages.urllib3
from requests.packages.urllib3.fields import RequestField, guess_content_type
import six
old_iter_field_objects = requests.packages.urllib3.filepost.iter_field_objects
def iter_field_objects(fields):
if isinstance(fields, dict):
i = six.iteritems(fields)
else:
i = iter(fields)
for field in i:
if isinstance(field, RequestField):
yield field
else:
name, value = field
filename = value[0]
data = value[1]
content_type = value[2] if len(value)>2 else guess_content_type(filename)
headers = None
if isinstance(content_type, (tuple, list)):
content_type, headers = content_type
rf = RequestField(name, data, filename, headers)
rf.make_multipart(content_type=content_type)
yield rf
requests.packages.urllib3.filepost.iter_field_objects = iter_field_objects
现在:
>>> files = {'file': ('foo.txt', 'foo\ncontents\n'),
... 'file2': ('bar.txt', 'bar contents', 'text/plain'),
... 'file3': ('baz.txt', 'baz contents', ('text/plain', {'header': 'value'}))}
>>. r = request.Request('POST', 'http://example.com', files=files)
>>> print r.prepare().body
--1ee28922d26146e7a2ee201e5bf22c44
Content-Disposition: form-data; name="file3"; filename="baz.txt"
Content-Type: text/plain
header: value
baz contents
--1ee28922d26146e7a2ee201e5bf22c44
Content-Disposition: form-data; name="file2"; filename="bar.txt"
Content-Type: text/plain
bar contents
--1ee28922d26146e7a2ee201e5bf22c44
Content-Disposition: form-data; name="file"; filename="foo.txt"
Content-Type: text/plain
foo
多田!
请注意,您需要相对最新的requests
/urllib3
才能正常工作。我认为requests
2.0.0 就足够了。