3

我目前正在对 HTTP 请求使用 Python请求,但由于 API 的限制,我无法继续使用该库。

我需要一个库,它允许我以类似流文件的方式编写请求正文,因为我将发送的数据不会立即可用,而且我想尽可能多地节省内存提出请求时。是否有一个易于使用的库允许我发送这样的 PUT 请求:

request = HTTPRequest()
request.headers['content-type'] = 'application/octet-stream'
# etc
request.connect()

# send body
with open('myfile', 'rb') as f:
    while True:
        chunk = f.read(64 * 1024)
        request.body.write(chunk)
        if not len(chunk) == 64 * 1024:
            break

# finish
request.close()

更具体地说,我有一个线程可以使用。使用这个线程,当我通过网络接收流时,我会收到回调。本质上,这些回调看起来像这样:

class MyListener(Listener):
    def on_stream_start(stream_name):
        pass

    def on_stream_chunk(chunk):
        pass

    def on_stream_end(total_size):
        pass

我需要在方法中创建我的上传请求,在on_stream_start方法中上传块on_stream_chunk,然后在方法中完成上传on_stream_end。因此,我需要一个支持write(chunk)类似以下方法的库:

class MyListener(Listener):
    request = None

    def on_stream_start(stream_name):
        request = RequestObject(get_url(), "PUT")
        request.headers.content_type = "application/octet-stream"
        # ...

    def on_stream_chunk(chunk):
        request.write_body(chunk + sha256(chunk).hexdigest())

    def on_stream_end(total_size):
        request.close()

requests库支持读取类似文件的对象和生成器,但不支持写出请求:拉而不是推。是否有一个库可以让我将数据向上推送到服务器?

4

4 回答 4

2

据我所知httplib,'sHTTPConnection.request正是你想要的。

我追踪了实际执行发送的函数,只要您传递的是类似文件的对象(而不是字符串),它就会将其分块:

Definition: httplib.HTTPConnection.send(self, data)
Source:

def send(self, data):
    """Send `data' to the server."""
    if self.sock is None:
        if self.auto_open:
            self.connect()
        else:
            raise NotConnected()

    if self.debuglevel > 0:
        print "send:", repr(data)
    blocksize = 8192
    if hasattr(data,'read') and not isinstance(data, array):
        if self.debuglevel > 0: print "sendIng a read()able"

        ## {{{ HERE IS THE CHUCKING LOGIC
        datablock = data.read(blocksize)
        while datablock:
            self.sock.sendall(datablock)
            datablock = data.read(blocksize)
        ## }}}

    else:
        self.sock.sendall(data)
于 2013-04-15T20:06:30.450 回答
2

我在我的代码库的几个地方做了这样的事情。您需要一个上传文件包装器,并且您需要另一个线程或 greenthread - 我在我的实例中使用 eventlet 进行假线程。Call requests.put,它将阻止read()您的类文件对象包装器。您调用的线程put将阻塞等待,因此您需要在另一个线程中进行接收。

抱歉没有发布代码,我只是在压缩时看到了这个。我希望这足以帮助,如果没有,也许我可以稍后编辑和添加更多。

于 2013-04-19T03:50:42.157 回答
1

Requests 实际上支持带files参数的多部分编码请求:

官方文档中的 Multipart POST 示例

url = 'http://httpbin.org/post'
files = {'file': open('report.xls', 'rb')}

r = requests.post(url, files=files)
r.text
{
  ...
  "files": {
    "file": "<censored...binary...data>"
  },
  ...
}

如果您愿意,您也可以创建自己的类似文件的流对象,但您不能在同一个请求中混合流和文件。

一个可能对您有用的简单案例是打开文件并返回一个基于生成器的分块阅读器:

def read_as_gen(filename, chunksize=-1): # -1 defaults to read the file to the end, like a regular .read()
    with open(filename, mode='rb') as f:
        while True:
            chunk = f.read(chunksize)
            if len(chunk) > 0:
                yield chunk
            else:
                raise StopIteration

# Now that we can read the file as a generator with a chunksize, give it to the files parameter
files = {'file': read_as_gen(filename, 64*1024)}

# ... post as normal.

但是如果你不得不阻止其他东西上的分块,比如另一个网络缓冲区,你可以用同样的方式处理它:

def read_buffer_as_gen(buffer_params, chunksize=-1): # -1 defaults to read the file to the end, like a regular .read()
    with buffer_open(*buffer_params) as buf: # some function to open up your buffer
    # you could also just pass in the buffer itself and skip the `with` block
        while True:
            chunk = buf.read(chunksize)
            if len(chunk) > 0:
                yield chunk
            else:
                raise StopIteration
于 2013-04-15T20:42:19.490 回答
-2

这可能会有所帮助

import urllib2

request = urllib2.Request(uri, data=data)
request.get_method = lambda: 'PUT' # or 'DELETE'
response = urllib2.urlopen(request)
于 2013-04-18T01:15:41.487 回答