使用Python进行HTTP POST二进制文件:简洁的非pycurl示例?

5 投票
5 回答
13848 浏览
提问于 2025-04-15 13:32

我想写一个简单的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 个回答

2

我知道这个问题已经很老了,但我有一个不同的解决办法。

如果你已经费了很多劲去构建那些复杂的头部信息,结果突然发现一个二进制文件无法通过,因为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文件。

3

今天我遇到了类似的问题,尝试了pycurl和multipart/form-data这两种方法后,我决定去看看python的httplib和urllib2的源代码,最终找到了一个比较好的解决方案:

  1. 在进行POST请求之前,先设置文件的Content-Length头信息。
  2. 在进行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/

4

上传文件需要使用一种叫做 multipart/form-data 的编码方式。就我所知,标准库里没有简单的方法(比如一行代码就能搞定)来做到这一点。不过,正如你提到的,网上有很多相关的教程。

虽然这些教程看起来比较复杂,但根据你的需求,你可以考虑把它封装成一个函数或类,这样就不用太担心了,对吧?可以看看ActiveState上的这个教程,并阅读评论区的建议:

或者看看这个 PyMOTW 中的 MultiPartForm 类,感觉也挺好用的:

我相信这两个方法都能处理二进制文件。

撰写回答