python-запросы, похоже, поддерживают пользовательские коллекции как список/сопоставление файлов для загрузки.
Это, в свою очередь, должно позволить добавлять пользовательские поля заголовков при загрузке каждого файла в пределах одной многостраничной загрузки.
Nope. Каждый файл в списке/сопоставлении должен быть файловым объектом (или строкой содержимого), 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 достаточно.
Awesome! Я могу придерживаться этого до сих пор и ходатайствовать о начале реализации кортежа-расширения, надеюсь, следующая версия поддерживает это :) –
@qarma: Если вы собираетесь [добавить запрос функции] (https://github.com/kennethreitz/request/issues/new), пожалуйста, сделайте это в ближайшее время и разместите ссылку здесь. Потому что я могу написать патч самостоятельно, и я не хочу создавать запрос на pull, который дублирует ваш запрос функции, но не связан с ним. – abarnert
https://github.com/kennethreitz/requests/issues/1639 –