Python: HTTP PUT с некодированными двоичными данными
Я не могу на всю жизнь выяснить, как выполнить HTTP-запрос PUT с дословными двоичными данными в Python 2.7 со стандартными библиотеками Python.
Я думал, что смогу сделать это с помощью urllib2, но не работает, потому что urllib2.Request
ожидает его данные в формате application/x-www-form-urlencoded
. Я не хочу кодировать двоичные данные, я просто хочу передать его дословно, после заголовков, которые включают
Content-Type: application/octet-stream
Content-Length: (whatever my binary data length is)
Это кажется таким простым, но я продолжаю крутиться и не могу понять, как это сделать.
Как я могу это сделать? (помимо открытия сырого двоичного сокета и записи на него)
Ответы
Ответ 1
Я выяснил свою проблему. Кажется, что есть неясное поведение в urllib2.Request
/urllib2.urlopen()
(по крайней мере, в Python 2.7)
Конструктор urllib2.Request(url, data, headers)
, кажется, ожидает, что тот же тип строки будет отображаться в параметрах url и data.
Я передавал исходные данные параметров данных из вызова read()
файла (который в Python 2.7 возвращает его в форме "простой" строки), но мой url был случайно Unicode, потому что я конкатенировал часть URL-адреса из результата другой функции, которая возвращает строки Unicode.
Вместо того, чтобы пытаться "downcast" url
из Unicode → простых строк, он пытался "ускорить" параметр data
в Unicode, и это дало мне ошибку кодека. (как это ни странно, это происходит при вызове функции urllib2.urlopen()
, а не в конструкторе urllib2.Request
)
Когда я изменил свой вызов функции на
# headers contains `{'Content-Type': 'application/octet-stream'}`
r = urllib2.Request(url.encode('utf-8'), data, headers)
он работал нормально.
Ответ 2
Вы неправильно читаете документацию: urllib2.Request
ожидает, что данные уже закодированы, а для POST это обычно означает формат application/x-www-form-urlencoded
. Вы можете связывать любые другие двоичные данные, например:
import urllib2
data = b'binary-data'
r = urllib2.Request('http://example.net/put', data,
{'Content-Type': 'application/octet-stream'})
r.get_method = lambda: 'PUT'
urllib2.urlopen(r)
Это приведет к желаемому запросу:
PUT /put HTTP/1.1
Accept-Encoding: identity
Content-Length: 11
Host: example.net
Content-Type: application/octet-stream
Connection: close
User-Agent: Python-urllib/2.7
binary-data
Ответ 3
Рассматривали/пытались использовать httplib?
HTTPConnection.request(метод, url [, body [, headers]])
Это отправит запрос на сервер с использованием метода HTTP-запроса метод и URL-адрес селектора. Если аргумент body присутствует, он должна быть строка данных для отправки после завершения заголовков. В качестве альтернативы, это может быть объект открытого файла, и в этом случае содержимое файла отправляется; этот файл должен поддерживать fileno() и read(). Заголовок Content-Length автоматически устанавливается на правильное значение. Аргумент заголовков должен быть отображением дополнительных HTTP-заголовки для отправки с запросом.
Ответ 4
Этот снимок сработал у меня, чтобы PUT изображение:
на сайте HTTPS. Если вам не нужен HTTPS, используйте
httplib.HTTPConnection(URL).
import httplib
import ssl
API_URL="api-mysight.com"
TOKEN="myDummyToken"
IMAGE_FILE="myimage.jpg"
imageID="myImageID"
URL_PATH_2_USE="/My/image/" + imageID +"?objectId=AAA"
headers = {"Content-Type":"application/octet-stream", "X-Access-Token": TOKEN}
imgData = open(IMAGE_FILE, "rb")
REQUEST="PUT"
conn = httplib.HTTPSConnection(API_URL, context=ssl.SSLContext(ssl.PROTOCOL_TLSv1))
conn.request(REQUEST, URL_PATH_2_USE, imgData, headers)
response = conn.getresponse()
result = response.read()