Ich versuche zu implementieren, was am besten als "eine FTP-Schnittstelle zu einer HTTP-API" beschrieben werden kann. Im Wesentlichen gibt es eine vorhandene REST-API, die zum Verwalten der Dateien eines Benutzers für eine Site verwendet werden kann, und ich erstelle einen Mediator-Server, der diese API als FTP-Server wieder verfügbar macht. So können Sie sich mit sagen, sagen, Filezilla und listet Ihre Dateien, neue hochladen, alte, etc.Twisted, FTP und "Streaming" große Dateien
Ich versuche dies mit twisted.protocols.ftp
für den (FTP) -Server und twisted.web.client
für den (HTTP) -Client .
Die Sache, gegen die ich stehe, ist, wenn ein Benutzer versucht, eine Datei herunterzuladen, diese Datei von einer HTTP-Antwort zu meiner FTP-Antwort "streaming". Ähnlich zum Hochladen.
Der einfachste Weg wäre, die gesamte Datei vom HTTP-Server herunterzuladen, dann umzudrehen und den Inhalt an den Benutzer zu senden. Das Problem dabei ist, dass jede Datei viele Gigabyte groß sein kann (denke Laufwerkbilder, ISO-Dateien usw.). Bei dieser Vorgehensweise würde der Inhalt der Datei jedoch zwischen dem Zeitpunkt, an dem ich sie von der API herunterlade, und dem Zeitpunkt, zu dem ich sie an den Benutzer sende, im Speicher gehalten - nicht gut.
Also meine Lösung ist es zu versuchen, "streamen" - wie ich Stücke von Daten aus der API-HTTP-Antwort bekomme, möchte ich nur umdrehen und diese Stücke an den FTP-Benutzer senden. Scheint einfach. Für meine "benutzerdefinierte FTP-Funktionalität" verwende ich eine Unterklasse von ftp.FTPShell
. Die Lesemethode davon, openForReading
, gibt ein Deferred zurück, das mit einer Implementierung von IReadFile
ausgelöst wird.
Unten ist meine (erste, einfache) Implementierung für "Streaming HTTP". Ich verwende die fetch
Funktion, um eine HTTP-Anfrage einzurichten, und der Callback, den ich übergebe, wird mit jedem Chunk aufgerufen, den ich von der Antwort bekomme.
Ich dachte, ich könnte eine Art zwei Ende Pufferobjekt verwenden, um die Chunks zwischen HTTP und FTP zu transportieren, indem Sie das Pufferobjekt als dateiähnliches Objekt von ftp._FileReader
benötigt, aber das erweist sich schnell als nicht funktionierend, wie der Verbraucher von der send
Aufruf fast sofort den Puffer schließt (weil es eine leere Zeichenfolge zurückgibt, weil es noch keine Daten zum Lesen usw.). Daher sende ich leere Dateien, bevor ich überhaupt die HTTP-Antwort-Chunks empfange.
Bin ich nah, aber etwas fehlt? Bin ich überhaupt auf dem falschen Weg? Ist das, was ich tun möchte wirklich unmöglich (ich bezweifle das stark)?
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)
Als Randbemerkung, ist dies mit Verdrehte meinem zweiten Tag nur - ich die meisten gestern verbrachte durch Twisted Introduction Dave Peticolas lesen, die ein guter Ausgangspunkt war, wenn auch auf einer älteren Version von Twisted-Basis .
Das sagte, ich kann Dinge falsch machen.
Sie hatten Recht, ich musste IPushProducer implementieren. Es funktioniert jetzt ziemlich gut, obwohl ich noch keine Sicherheitsmaßnahmen für das von Ihnen erwähnte Szenario "Fast HTTP" habe. Vielen Dank! – eternicode