python: 使用未编码的二进制数据进行HTTP PUT

11 投票
4 回答
12646 浏览
提问于 2025-04-17 09:24

我实在搞不懂怎么用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 个回答

4

你有没有考虑过或者尝试过使用 httplib 呢?

HTTPConnection.request(method, url[, body[, headers]])

这个方法会向服务器发送一个请求,使用的是你指定的HTTP请求方法和网址。如果你提供了body参数,它应该是一个字符串,表示在发送完头信息后要发送的数据。或者,你也可以传一个打开的文件对象,这样就会把文件的内容发送出去;这个文件对象需要支持fileno()和read()这两个方法。系统会自动把Content-Length这个头信息设置为正确的值。headers参数应该是一个额外的HTTP头信息的映射,用来和请求一起发送。

9

你可能误解了文档的意思: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
12

我找到了我的问题。看起来在 urllib2.Requesturllib2.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)

之后,一切就正常了。

撰写回答