Twisted,FTP和“流式传输”大文件

时间:2010-11-19 05:14:57

标签: python ftp twisted

我正在尝试实现最好被描述为“HTTP API的FTP接口”。本质上,有一个现有的REST API可用于管理站点的用户文件,我正在构建一个中介服务器,将该API重新公开为FTP服务器。因此,您可以使用Filezilla登录并列出您的文件,上传新文件,删除旧文件等。

我正在尝试将twisted.protocols.ftp用于(FTP)服务器,并twisted.web.client用于(HTTP)客户端。

我正在遇到的问题是,当用户尝试下载文件时,将该文件从HTTP响应“流式传输”到我的FTP响应中。类似于上传。

最直接的方法是从HTTP服务器下载整个文件,然后转身并将内容发送给用户。这个问题是任何给定的文件都可能是几千兆字节(想想驱动器映像,ISO文件等)。但是,通过这种方法,文件的内容将在我从API下载它的时间和我发送给用户的时间之间保存在内存中 - 不是很好。

所以我的解决方案是尝试“流式传输” - 当我从API的HTTP响应中获取大量数据时,我只想转身并将这些块发送给FTP用户。 似乎直截了当。

对于我的“自定义FTP功能”,我使用的是ftp.FTPShell的子类。读取方法openForReading将返回一个延迟,使用IReadFile的实现进行触发。

以下是“流式HTTP”的(初始,简单)实现。我使用fetch函数来设置HTTP请求,并且我传入的回调会被我从响应中获得的每个块调用。

我认为我可以使用某种双端缓冲区对象来传输HTTP和FTP之间的块,方法是将缓冲区对象用作ftp._FileReader所需的类文件对象,但这很快就证明不是工作,因为send调用的消费者几乎立即关闭缓冲区(因为它返回一个空字符串,因为还没有数据可读,等等)。因此,在我开始接收HTTP响应块之前,我正在“发送”空文件。

我是否关闭,但遗漏了什么?我完全走错了路吗?我想做什么真的不可能(我非常怀疑)?

from twisted.web import client
import urlparse

class HTTPStreamer(client.HTTPPageGetter):
    def __init__(self):
        self.callbacks = []

    def addHandleResponsePartCallback(self, callback):
        self.callbacks.append(callback)

    def handleResponsePart(self, data):
        for cb in self.callbacks:
            cb(data)
        client.HTTPPageGetter.handleResponsePart(self, data)

class HTTPStreamerFactory(client.HTTPClientFactory):
    protocol = HTTPStreamer

    def __init__(self, *args, **kwargs):
        client.HTTPClientFactory.__init__(self, *args, **kwargs)
        self.callbacks = []

    def addChunkCallback(self, callback):
        self.callbacks.append(callback)

    def buildProtocol(self, addr):
        p = client.HTTPClientFactory.buildProtocol(self, addr)
        for cb in self.callbacks:
            p.addHandleResponsePartCallback(cb)
        return p

def fetch(url, callback):

    parsed = urlparse.urlsplit(url)

    f = HTTPStreamerFactory(parsed.path)
    f.addChunkCallback(callback)

    from twisted.internet import reactor
    reactor.connectTCP(parsed.hostname, parsed.port or 80, f)

作为旁注,这只是我在Twisted的第二天 - 我昨天大部分时间通过Dave Peticolas'Twisted Introduction阅读,这是一个很好的起点,即使是基于旧版本的扭曲

那就是说,我可能做错了。

1 个答案:

答案 0 :(得分:1)

  

我认为我可以使用某种双端缓冲区对象来传输HTTP和FTP之间的块,方法是使用缓冲区对象作为ftp._FileReader所需的类文件对象,但这很快就证明不起作用,因为来自send调用的消费者几乎立即关闭缓冲区(因为它返回一个空字符串,因为还没有数据要读取,等等)。因此,在我开始接收HTTP响应块之前,我正在“发送”空文件。

除了使用ftp._FileReader之外,您还需要一些东西,只要块从您的HTTPStreamer到达它提供的回调,就会执行写操作。你永远不需要/想要从HTTP上的缓冲区读取,因为没有理由甚至没有这样的缓冲区。一旦HTTP字节到达,就将它们写入消费者。有点像...

class FTPStreamer(object):
    implements(IReadFile)

    def __init__(self, url):
        self.url = url

    def send(self, consumer):
        fetch(url, consumer.write)
        # You also need a Deferred to return here, so the 
        # FTP implementation knows when you're done.
        return someDeferred

您可能还希望使用Twisted的生产者/消费者界面来限制传输,如果您与HTTP服务器的连接速度快于用户与您的FTP连接,则可能需要这样做。