2010-11-19 52 views
3

我试图实现什么可以最好地描述为“HTTP API的FTP接口”。从本质上讲,现有的REST API可以用来管理站点的用户文件,而且我正在构建一个中介服务器,将该API重新公开为FTP服务器。所以,你可以用,比如说,FileZilla中登录并列出您的文件,上传新的,删除旧等扭曲,FTP和“流”大文件

我试图用这个为twisted.protocols.ftp的(FTP)服务器,并twisted.web.client为(HTTP)客户端。

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

最直接的方法是从HTTP服务器下载整个文件,然后转向并将内容发送给用户。这样做的问题是,任何给定的文件可能有很多千兆字节(考虑驱动器映像,ISO文件等)。但是,使用这种方法时,文件的内容将在我从API下载它的时间到将其发送给用户的时间内保存在内存中 - 这并不好。

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

对于我的“自定义FTP功能”,我使用的是ftp.FTPShell的子类。这个openForReading的读取方法返回一个Deferred,其实现为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) 

作为一个方面说明,这仅仅是我的第二天与扭曲 - 我的大部分时间都昨日通过阅读戴夫Peticolas' Twisted Introduction,这即使基于旧版本的扭曲是一个很好的起点, 。

这就是说,我可能是做错事。

回答

1

我想我可以使用某种形式的双端缓存的对象,通过使用缓冲区对象由ftp._FileReader所需的类文件对象运输HTTP和FTP之间的块,但是这很快证明不会工作,因为来自发送调用的消费者几乎立即关闭缓冲区(因为它返回一个空字符串,因为没有数据要读取等等)。因此,在我开始接收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连接,更快的可能是必要的您。

+0

你说得对,我需要实现'IPushProducer'。它现在运行得很好,但我还没有为你提到的“快速HTTP”场景提供保护。谢谢! – eternicode 2010-11-19 19:49:29