【问题标题】:Asynchronous download of files with twisted and (tx)requests带有扭曲和(tx)请求的文件的异步下载
【发布时间】:2018-05-10 08:53:54
【问题描述】:

我正在尝试从一个扭曲的应用程序中从 Internet 下载文件。我想使用请求来执行此操作,因为它直接提供或具有维护良好的库来提供(重试、代理、缓存控制等)。我对没有这些功能的唯一扭曲解决方案持开放态度,但无论如何我似乎都找不到。

文件应该相当大,并且会在慢速连接时下载。因此,我使用请求的stream=True 接口和响应的 iter_content。本问题末尾列出了或多或少完整的代码片段。其入口点是http_download 函数,调用时使用urldst 将文件写入,callback 和可选errback 用于处理失败的下载。我已经删除了一些与准备目的地有关的代码(创建文件夹等)和在反应器退出期间关闭会话的代码,但我认为它仍然应该按原样工作。

此代码有效。文件下载完毕,双绞反应器继续运行。但是,我似乎对这段代码有疑问:

def _stream_download(r, f):
    for chunk in r.iter_content(chunk_size=128):
        f.write(chunk)
        yield None

cooperative_dl = cooperate(_stream_download(response, filehandle))

因为iter_content 仅在它有一个块要返回时才返回,所以反应器处理一个块,运行其他代码,然后返回等待下一个块,而不是让自己忙于更新 GUI 上的旋转等待动画(代码实际上并未在此处发布)。

问题来了——

  • 有没有一种方法可以扭曲这个生成器,以便在生成器本身不准备让出某些东西时让出控制权?我遇到了一些twisted.flow 的文档,这似乎很合适,但这似乎并没有使它变得扭曲或今天不再存在。这个问题可以独立于细节来阅读,即,关于任何任意阻塞生成器,或者可以在问题的直接上下文中阅读。
  • 有没有办法扭曲以使用请求等功能齐全的东西异步下载文件?是否有一个现有的扭曲模块可以做到这一点,我可以使用吗?
  • 解决这种扭曲问题的基本方法是什么,与我想从请求中使用的 http 功能无关。假设我准备放弃它们或以其他方式实施它们。如何通过 HTTP 异步下载文件。
import os
import re
from functools import partial
from six.moves.urllib.parse import urlparse

from requests import HTTPError
from twisted.internet.task import cooperate
from txrequests import Session

class HttpClientMixin(object):
    def __init__(self, *args, **kwargs):
        self._http_session = None

    def http_download(self, url, dst, callback, errback=None, **kwargs):
        dst = os.path.abspath(dst)
        # Log request
        deferred_response = self.http_session.get(url, stream=True, **kwargs)
        deferred_response.addCallback(self._http_check_response)
        deferred_response.addCallbacks(
            partial(self._http_download, destination=dst, callback=callback),
            partial(self._http_error_handler, url=url, errback=errback)
        )

    def _http_download(self, response, destination=None, callback=None):
        def _stream_download(r, f):
            for chunk in r.iter_content(chunk_size=128):
                f.write(chunk)
                yield None

        def _rollback(r, f, d):
            if r:
                r.close()
            if f:
                f.close()
            if os.path.exists(d):
                os.remove(d)

        filehandle = open(destination, 'wb')
        cooperative_dl = cooperate(_stream_download(response, filehandle))
        cooperative_dl.whenDone().addCallback(lambda _: response.close)
        cooperative_dl.whenDone().addCallback(lambda _: filehandle.close)
        cooperative_dl.whenDone().addCallback(
            partial(callback, url=response.url, destination=destination)
        )
        cooperative_dl.whenDone().addErrback(
            partial(_rollback, r=response, f=filehandle, d=destination)
        )

    def _http_error_handler(self, failure, url=None, errback=None):
        failure.trap(HTTPError)
        # Log error message
        if errback:
            errback(failure)

    @staticmethod
    def _http_check_response(response):
        response.raise_for_status()
        return response

    @property
    def http_session(self):
        if not self._http_session:
            # Log session start
            self._http_session = Session()
        return self._http_session

【问题讨论】:

    标签: python asynchronous python-requests twisted


    【解决方案1】:

    有没有办法扭曲这个生成器,当生成器本身没有准备好产生某些东西时,它会产生控制权?

    没有。 Twisted 所能做的就是调用代码。如果代码无限期阻塞,则调用线程将无限期阻塞。这是 Python 运行时的基本前提。

    有没有办法扭曲使用请求等功能齐全的东西异步下载文件?

    treq。您没有在这里说“全功能”是什么意思,但之前您提到了“重试”、“代理”和“缓存控制”。我不相信treq 目前有这些功能。您可以在treq 文档中找到some kind of feature matrix(尽管我注意到它不包含您提到的任何功能——即使是请求)。我希望这些功能的实现会作为 Treq 贡献受到欢迎。

    有没有办法让使用请求等功能齐全的东西异步下载文件?

    在线程中运行它 - 可能使用 Twisted 的线程池 API。

    解决这种扭曲问题的基本方法是什么,独立于我想从请求中使用的 http 功能。

    treq.

    【讨论】:

    • 谢谢。我会试试treq。既然你问了,我使用的请求功能更多的是它提供的钩子使其成为可能的功能。具体来说,请求会话对象,它可以使用带有 HttpAdapters 的 session.mount 接口指定重试行为。我喜欢使用的其他适配器是 CacheControl 的适配器,它可以让我引入可自定义的缓存行为,以及 session.proxies 接口,它让我无需在代码的“用户”端执行任何其他操作即可使用网络代理。
    • 另外,能够使用 raise_for_response() 类型功能引发 HTTP 错误。
    猜你喜欢
    • 1970-01-01
    • 2017-08-09
    • 2018-07-15
    • 1970-01-01
    • 2012-02-25
    • 2013-10-23
    • 1970-01-01
    • 2011-05-31
    相关资源
    最近更新 更多