使用Python进行HTTP POST二进制文件:简洁的非pycurl示例?
我想写一个简单的Python脚本,用来通过POST请求把一个小的二进制文件(比如.wav或.raw音频文件)上传到远程服务器。
我之前用过pycurl,这样做很简单,脚本也很简洁;不过不幸的是,使用这个的用户必须安装pycurl,而我不能指望每个人都有这个。
我还看到一些其他帖子里的例子,它们只用了一些基础库,比如urllib、urllib2等等,但这些代码通常比较冗长,我也想避免这种情况。
我在想有没有一些简洁的例子,不需要外部库,而且第三方也能快速理解——即使他们对Python不太熟悉。
我现在用的代码大致是这样的:
def upload_wav( wavfile, url=None, **kwargs ):
"""Upload a wav file to the server, return the response."""
class responseCallback:
"""Store the server response."""
def __init__(self):
self.contents=''
def body_callback(self, buf):
self.contents = self.contents + buf
def decode( self ):
self.contents = urllib.unquote(self.contents)
try:
self.contents = simplejson.loads(self.contents)
except:
return self.contents
t = responseCallback()
c = pycurl.Curl()
c.setopt(c.POST,1)
c.setopt(c.WRITEFUNCTION, t.body_callback)
c.setopt(c.URL,url)
postdict = [
('userfile',(c.FORM_FILE,wavfile)), #wav file to post
]
#If there are extra keyword args add them to the postdict
for key in kwargs:
postdict.append( (key,kwargs[key]) )
c.setopt(c.HTTPPOST,postdict)
c.setopt(c.VERBOSE,verbose)
c.perform()
c.close()
t.decode()
return t.contents
这不是完全准确,但能给你一个大概念。它运行得很好,对第三方来说也很简单理解,但是它需要pycurl。
5 个回答
我知道这个问题已经很老了,但我有一个不同的解决办法。
如果你已经费了很多劲去构建那些复杂的头部信息,结果突然发现一个二进制文件无法通过,因为Python库不太友好,你可以用一种“猴子补丁”的方法来解决这个问题。
import httplib
class HTTPSConnection(httplib.HTTPSConnection):
def _send_output(self, message_body=None):
self._buffer.extend(("",""))
msg = "\r\n".join(self._buffer)
del self._buffer[:]
self.send(msg)
if message_body is not None:
self.send(message_body)
httplib.HTTPSConnection = HTTPSConnection
如果你使用的是HTTP://而不是HTTPS://,那么就把上面所有的HTTPSConnection替换成HTTPConnection。
在大家对我不满之前,我得说,这确实是一个糟糕的解决方案,但如果你真的不想重新设计现有的代码,这也是一个可以修复的办法。
为什么这样做能解决问题呢?去看看原始的Python源代码,httplib.py文件。
今天我遇到了类似的问题,尝试了pycurl和multipart/form-data这两种方法后,我决定去看看python的httplib和urllib2的源代码,最终找到了一个比较好的解决方案:
- 在进行POST请求之前,先设置文件的Content-Length头信息。
- 在进行POST请求时,传入一个打开的文件。
下面是代码:
import urllib2, os
image_path = "png\\01.png"
url = 'http://xx.oo.com/webserviceapi/postfile/'
length = os.path.getsize(image_path)
png_data = open(image_path, "rb")
request = urllib2.Request(url, data=png_data)
request.add_header('Cache-Control', 'no-cache')
request.add_header('Content-Length', '%d' % length)
request.add_header('Content-Type', 'image/png')
res = urllib2.urlopen(request).read().strip()
return res
可以查看我的博客文章: http://www.2maomao.com/blog/python-http-post-a-binary-file-using-urllib2/
上传文件需要使用一种叫做 multipart/form-data
的编码方式。就我所知,标准库里没有简单的方法(比如一行代码就能搞定)来做到这一点。不过,正如你提到的,网上有很多相关的教程。
虽然这些教程看起来比较复杂,但根据你的需求,你可以考虑把它封装成一个函数或类,这样就不用太担心了,对吧?可以看看ActiveState上的这个教程,并阅读评论区的建议:
或者看看这个 PyMOTW 中的 MultiPartForm
类,感觉也挺好用的:
我相信这两个方法都能处理二进制文件。