【问题标题】:python: HTTP PUT with binary datapython:带有二进制数据的 HTTP PUT
【发布时间】:2011-11-02 15:51:47
【问题描述】:

所以我按照answers to another question 的建议调整了 urllib2:

class HttpRequest(urllib2.Request):
  def __init__(self, *args, **kwargs):
    self._method = kwargs.pop('method', 'GET')
    urllib2.Request.__init__(self, *args, **kwargs)
  def get_method(self):
    return self._method

它适用于带有 JSON 的 PUT:

req = HttpRequest(url=url, method='PUT', 
    data=json.dumps(metadata))
response = urllib2.urlopen(req)

但它失败了data=二进制数据(下面的部分堆栈跟踪):

  File "c:\appl\python\2.7.2\lib\urllib2.py", line 126, in urlopen
    return _opener.open(url, data, timeout)
  File "c:\appl\python\2.7.2\lib\urllib2.py", line 394, in open
    response = self._open(req, data)
  File "c:\appl\python\2.7.2\lib\urllib2.py", line 412, in _open
    '_open', req)
  File "c:\appl\python\2.7.2\lib\urllib2.py", line 372, in _call_chain
    result = func(*args)
  File "c:\appl\python\2.7.2\lib\urllib2.py", line 1199, in http_open
    return self.do_open(httplib.HTTPConnection, req)
  File "c:\appl\python\2.7.2\lib\urllib2.py", line 1168, in do_open
    h.request(req.get_method(), req.get_selector(), req.data, headers)
  File "c:\appl\python\2.7.2\lib\httplib.py", line 955, in request
    self._send_request(method, url, body, headers)
  File "c:\appl\python\2.7.2\lib\httplib.py", line 989, in _send_request
    self.endheaders(body)
  File "c:\appl\python\2.7.2\lib\httplib.py", line 951, in endheaders
    self._send_output(message_body)
  File "c:\appl\python\2.7.2\lib\httplib.py", line 809, in _send_output
    msg += message_body
UnicodeDecodeError: 'ascii' codec can't decode byte 0xe2 in position 10: ordinal
 not in range(128)

有什么办法可以解决这个问题吗?

【问题讨论】:

  • metadata 的类型是什么?如果是unicode,先编码成某种编码。
  • 这是一个文件(pdf 或 jpg 之类的),可能有几兆字节,所以我正在寻找高效的文件。如果 urllib2 不是,那好吧。

标签: python http binary put


【解决方案1】:

因为

data 应该是标准 application/x-www-form-urlencoded 格式的缓冲区。 urllib.urlencode() 函数采用 2 元组的映射或序列,并以这种格式返回一个字符串。

来自 urllib2 文档

【讨论】:

  • 该文档的措辞在这方面有些误导。数据应该已经编码,并且通常在POST请求中意味着application/x-www-form-urlencoded
【解决方案2】:

您正在尝试将 python unicode 字符串自动转换为常规字节字符串。 JSON 始终是 unicode,但 HTTP 必须发送字节。如果您确信接收方会理解特定编码中的 json 编码数据,则可以这样编码:

>>> urllib2.urlopen(urllib2.Request("http://example.com", data=u'\u0ca0'))
Traceback (most recent call last):
  ...
UnicodeEncodeError: 'ascii' codec cannot encode character u'\u0ca0' in position 0: ordinal not in range(128)
>>> urllib2.urlopen(urllib2.Request("http://example.com", 
...                                 data=u'\u0ca0'.encode('utf-8')))
<addinfourl at 15700984 whose fp = <socket._fileobject object at 0xdfbe50>>
>>> 

注意.encode('utf-8'),它将unicode 转换为utf-8 中的str。隐式转换将使用 ascii,它不能编码非 ascii 字符。

tl;博士... data=json.dumps(blabla).encode('utf-8') ...

【讨论】:

  • 它不是 json,我只是举一个例子,它可以正常处理非二进制数据。
  • Unicode 不是二进制的。要修复错误,您应该将数据加载为 str (python 3, bytes) 或以某种方式将其转换为此类。
【解决方案3】:

根据urllib2 documentation,您需要对字节字符串进行百分比编码。

【讨论】:

  • 你能找到那个吗?正如您在@TokenMacGuy 的回答中看到的那样,这根本不是真的。
  • 考虑到公认的正确答案是一致的,您的评论(我认为对此表示反对)令人惊讶。在我链接到的文档中,您会发现:“数据应该是标准 application/x-www-form-urlencoded 格式的缓冲区。urllib.urlencode() 函数采用 2 元组的映射或序列并返回这种格式的字符串。”说到Request 构造函数的第二个参数。如果某处的服务器碰巧接受了 HTTP GET 请求中的任意字节,那很好,但据我所知,这将是一种非标准行为。
  • 或者如果Request 构造函数为您应用urlencode 操作(与percent-encode 相同),那么这将使TokenMacGuy 的代码能够正常工作,但这也将是一个文档错误,我的引用仍然没有问题。
  • 从您对接受的正确答案的评论来看,我想知道您是否混淆了字符编码和百分比编码。百分比编码(或 URL 编码)用于将字节值编码为字符。所以%41 不是“A”,而是 0x41。百分比编码不会告诉您字节的含义,只告诉您它们的值。所以 URL 编码 U+0101 是一个两步过程:U+0101 --> 0xc4 0x81(如果使用 UTF-8) --> %C4%81。因此需要两种转换。
  • 也许我不应该对你投反对票:虽然答案是错误的,但它是基于误导性文档。 Request对数据应用任何转换。 如果数据是典型表单提交的表示,则需要对其进行正确编码。在提交表单的情况下,这需要将其编码为字节元组列表(例如,UTF-8),然后进行百分比编码(并添加&amp;= 作为元组之间的粘合剂) . [继续..]
猜你喜欢
  • 1970-01-01
  • 2010-10-03
  • 2012-02-10
  • 2016-06-15
  • 1970-01-01
  • 1970-01-01
  • 2019-04-25
  • 2014-07-15
  • 1970-01-01
相关资源
最近更新 更多