Я пытаюсь реализовать то, что лучше всего можно описать как «интерфейс FTP к HTTP API». По сути, существует существующий REST API, который можно использовать для управления файлами пользователя на сайте, и я создаю сервер-посредник, который повторно предоставляет этот API как FTP-сервер. Таким образом, вы можете войти в систему, скажем, с помощью Filezilla и составить список своих файлов, загрузить новые, удалить старые и т. Д.
Я пытаюсь сделать это с twisted.protocols.ftp
для (FTP) сервера и twisted.web.client
для (HTTP) клиента.
Я сталкиваюсь с тем, что когда пользователь пытается загрузить файл, он «передает» этот файл из HTTP-ответа на мой FTP-ответ. Аналогично для загрузки.
Самый простой подход - загрузить весь файл с HTTP-сервера, а затем развернуть и отправить содержимое пользователю. Проблема заключается в том, что любой данный файл может иметь размер в несколько гигабайт (например, образы дисков, файлы ISO и т. Д.). Однако при таком подходе содержимое файла будет храниться в памяти между моментом, когда я загружаю его из API, и моментом, когда я отправляю его пользователю - не очень хорошо.
Итак, мое решение - попытаться «передать» его - поскольку я получаю фрагменты данных из HTTP-ответа API, я просто хочу развернуться и отправить эти фрагменты пользователю FTP. Кажется простым.
Для моей «настраиваемой функции FTP» я использую подкласс ftp.FTPShell
. Метод чтения этого openForReading
возвращает Deferred, который запускается с реализацией _ 5_.
Ниже моя (начальная, простая) реализация для "потокового HTTP". Я использую функцию fetch
для настройки HTTP-запроса, и переданный мной обратный вызов вызывается с каждым фрагментом, который я получаю из ответа.
Я подумал, что могу использовать своего рода двусторонний буферный объект для передачи фрагментов между HTTP и FTP, используя буферный объект как файловый объект, необходимый для _ 7_, но это быстро доказывает, что не работает, поскольку потребитель из вызова 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 - большую часть вчерашнего дня я провел, читая Дэйва Петиколаса. Twisted Introduction, который стал отличной отправной точкой, даже если он основан на более старой версии twisted.
Тем не менее, я может что-то делаю неправильно.
IPushProducer
. Сейчас он работает довольно хорошо, хотя у меня пока нет гарантий для упомянутого вами сценария быстрого HTTP. Спасибо! 19.11.2010