Хобрук: Ваш путь к мастерству в программировании

Скрученный, FTP и потоковая передача больших файлов

Я пытаюсь реализовать то, что лучше всего можно описать как «интерфейс 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.

Тем не менее, я может что-то делаю неправильно.

19.11.2010

Ответы:


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-соединение вашего пользователя с вами.

19.11.2010
  • Вы были правы, мне нужно было реализовать IPushProducer. Сейчас он работает довольно хорошо, хотя у меня пока нет гарантий для упомянутого вами сценария быстрого HTTP. Спасибо! 19.11.2010
  • Новые материалы

    Понимание структуры каталогов Linux
    Структура каталогов Linux — это иерархическая организация файлов и каталогов в системе Linux. Понимание этой структуры важно для всех, кто хочет использовать Linux в качестве основной..

    Введение в SQLite3 в Python
    SQLite3 — это популярная и часто используемая программная библиотека для управления реляционными базами данных. Она портативна, быстра и проста в использовании, что делает ее отличным выбором..

    Как создать ORM в Node.js
    Что такое ОРМ? Проще говоря, система ORM — это метод, в котором вы используете объектно-ориентированную парадигму для создания сопоставления между приложением и базой данных для выполнения..

    Руководство для начинающих по обработке естественного языка .
    Обработка естественного языка — захватывающая и быстро развивающаяся область, которую можно охарактеризовать как сочетание лингвистики, информатики и искусственного интеллекта. Это помогает..

    Защитите свое приложение NodeJS с помощью веб-токенов JSON (JWT): руководство по аутентификации на основе токенов
    В современном мире веб-приложений безопасность важнее, чем когда-либо. Одним из наиболее важных аспектов защиты веб-приложений является аутентификация пользователей. Аутентификация на основе..

    Вводное руководство по аннотациям @PreAuthorize и @PostAuthorize в Spring Security
    Введение Spring Security — это мощная и настраиваемая среда аутентификации и контроля доступа для приложений Java, особенно тех, которые созданы с использованием среды Spring. Этот пост..

    От стартапов к успеху в индустрии наук о данных: 5 важных инструментов
    Постоянно растущие объемы данных сегодня играют решающую роль для каждой компании, которая хочет оставаться на вершине. Это означает использование технологий искусственного интеллекта ,..