python: 使用未编码的二进制数据进行HTTP PUT
我实在搞不懂怎么用Python 2.7的标准库发送一个包含原始二进制数据的HTTP PUT请求。
我原以为可以用urllib2来实现,但这行不通,因为urllib2.Request
要求数据是application/x-www-form-urlencoded
格式的。我不想对二进制数据进行编码,我只是想在包含头信息之后,原封不动地传输它。
Content-Type: application/octet-stream
Content-Length: (whatever my binary data length is)
这看起来很简单,但我一直在绕圈子,怎么也想不明白该怎么做。
我该怎么做呢?(除了打开一个原始的二进制套接字并写入数据)
4 个回答
你有没有考虑过或者尝试过使用 httplib 呢?
HTTPConnection.request(method, url[, body[, headers]])
这个方法会向服务器发送一个请求,使用的是你指定的HTTP请求方法和网址。如果你提供了body参数,它应该是一个字符串,表示在发送完头信息后要发送的数据。或者,你也可以传一个打开的文件对象,这样就会把文件的内容发送出去;这个文件对象需要支持fileno()和read()这两个方法。系统会自动把Content-Length这个头信息设置为正确的值。headers参数应该是一个额外的HTTP头信息的映射,用来和请求一起发送。
你可能误解了文档的意思: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
我找到了我的问题。看起来在 urllib2.Request
和 urllib2.urlopen()
这两个地方(至少在 Python 2.7 中)有一些不太明显的行为。
这个 urllib2.Request(url, data, headers)
的构造函数似乎期望它的 url 和 data 参数都是同一种类型的字符串。
我给 data 参数传入的是从文件中读取的原始数据 read()
(在 Python 2.7 中,这会返回一种“普通”的字符串),但我的 url 不小心用了 Unicode,因为我把 URL 的一部分和另一个返回 Unicode 字符串的函数的结果拼接在了一起。
它不是试图把 url
从 Unicode 转换成普通字符串,而是试图把 data
参数转换成 Unicode,这导致了一个编码错误。(奇怪的是,这个错误发生在 urllib2.urlopen()
函数调用时,而不是在 urllib2.Request
的构造函数中)
当我把函数调用改成
# headers contains `{'Content-Type': 'application/octet-stream'}`
r = urllib2.Request(url.encode('utf-8'), data, headers)
之后,一切就正常了。