2010-11-19 10 views
3

「HTTP APIへのFTPインターフェイス」として最もよく記述できるものを実装しようとしています。基本的には、サイトのユーザーファイルを管理するために使用できる既存のREST APIがあります。このAPIをFTPサーバーとして再公開するメディエータサーバーを構築しています。したがって、Filezillaなどでログインしてファイルを一覧表示したり、新しいファイルをアップロードしたり、古いファイルを削除したりすることができます。大きなファイルをTwisted、FTP、 "streaming"する

(FTP)サーバではtwisted.protocols.ftp、HTTPクライアントではtwisted.web.client

私が対抗しているのは、ユーザーがファイルをダウンロードしようとすると、そのファイルをHTTP応答からFTP応答に「ストリーミング」することです。アップロードと同様です。

最も簡単なアプローチは、HTTPサーバーからファイル全体をダウンロードしてから、内容を変更してユーザーに送信することです。この問題は、任意のファイルが何ギガバイトも大きくなることがある(ドライブイメージ、ISOファイルなどを考える)ことがあります。しかし、このアプローチでは、ファイルの内容は、APIからダウンロードしてからユーザーに送信するまでの間にメモリに保持されます。

私の解決策は、APIのHTTPレスポンスからデータのチャンクを取得するときに、そのチャンクをFTPユーザーに送信したいだけです。 と思われます。

私の「カスタムFTP機能」については、ftp.FTPShellのサブクラスを使用しています。この読み取りメソッドopenForReadingは、実装がIReadFileのDeferredを返します。

以下は、「ストリーミングHTTP」用の私の(初期の、単純な)実装です。私はfetch関数を使ってHTTPリクエストを設定し、私が渡すコールバックはレスポンスから取得した各チャンクで呼び出されます。

私は、バッファオブジェクトをftp._FileReaderで必要とされるファイルのようなオブジェクトとして使用して、HTTPとFTPの間でチャンクを転送するために何らかの2端バッファオブジェクトを使用できると思っていましたが、 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の間にチャンクを輸送するために、2つのエンドのバッファオブジェクトのいくつかの並べ替えを使用することができると思ったが、それはすぐに証明しています送信呼び出しのコンシューマはすぐにバッファを閉じます(空の文字列を返すため、読み込むデータがないなど)。したがって、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 

ます。また、HTTPサーバへの接続をするユーザーのFTP接続よりも高速である場合に必要になる可能性があるため、転送を絞ることができるようにツイストのプロデューサ/コンシューマインタフェースを使用することもできます君は。

+0

あなたは正しく、私は 'IPushProducer'を実装する必要がありました。私はあなたが言及した "高速HTTP"シナリオのための保護手段をまだ持っていませんが、かなりうまくいきます。ありがとう! – eternicode

関連する問題