Простой файловый хостинг на Google App Engine

Наверняка каждый из вас в своей жизни находил удобный для себя файловый хостинг, а через какое-то время обнаруживал, что на нем от количества рекламы начинают болеть глаза, условия уже далеко не такие лояльные и вообще пора бы уже найти что-то новое. Вариантов дальнейших действий два — или найти новый, пока еще не раскрученный файлообменник и использовать его, пока он не испортится, или организовать собственное решение. Для второго варианта, в свою очередь, можно приобрести хостинг (придется правда набить шишек, пока не найдется добросовестный хостер с качественными услугами) или воспользоваться облачным сервисом.

Довольно интересной находкой оказался PaaS-хостинг от Google — Google App Engine (далее GAE), который дает возможность хранить до 5 Гб файлов при 1 Гб входящего и 1 Гб исходящего трафика в день, и кроме всего прочего, в нем используется модель High Replication, то есть ваши данные будут хранится сразу на нескольких серверах по всему миру!
Особенностью GAE является несколько нестандартный интерфейс для работы с файлами, поэтому я и сделал собственный сервис, о чем расскажу в данной статье.

GAE дает возможность создавать приложения на Java, Go и Python. Так как первые два языка для меня почти незнакомы, я написал свой сервис на Python. Ранее можно было использовать только версию 2.5, что создавало определенные сложности, но недавно добавили поддержку 2.7, поэтому теперь не нужно вспоминать устаревшие подходы.

Чтобы начать использовать GAE, скачайте и установите Python версии 2.7, а также appengine SDK для своей операционной системы. Повествование будет вестись на примере версии SDK для Linux (в Windows и Mac есть удобный графический интерфейс, так что разобраться будет несложно, хотя можно делать все из консоли, как описано ниже).

Чтобы в дальнейшем было проще с настройками, рекомендуется заранее зарегистрировать приложение на http://appengine.google.com, нажав кнопку “Create application”. Вам предложат ввести уникальный идентификатор приложения, по которому оно будет доступно как поддомен appspot.com, а также название, которое в дальнейшем можно будет менять, в отличии от идентификатора. Все остальные настройки можно не трогать, в нашем случае они не имеют особого значения. Для начала работы, создайте папку с именем, которое соответствует вашему идентификатору, в которой будут храниться все файлы, относящиеся к приложению.

Любое приложение состоит, как минимум, из файла app.yaml, в котором размещены название приложения, версия исполняемой среды и описание обработчиков URL и различных ошибок. Обработчик URL состоит из регулярного выражения, с помощью которого проверяются ссылки, а также описания необходимых файлов. Отмечу, что в именах скриптов для обработчиков, теперь нужно ставить расширение (которое вообщем-то уже и не расширение, а объект в приложении) не ‘.py’, а ‘.app’

Для нашего приложения файл app.yaml имеет следующий вид:

application: fileshare # здесь вместо fileshare нужно подставить свой идентификатор
version: 1
runtime: python27
api_version: 1
threadsafe: true

handlers:  
- url: /favicon\.ico
  static_files: favicon.ico
  upload: favicon\.ico
  
- url: /delete/.* # Ссылка для удаления файла
  script: main.app
  login: admin # Размещать и удалять файлы сможет только администратор приложения.

- url: /get/.* # ссылка для прямого доступа к файлу, открывать может кто угодно, хотя если добавить login: admin или login: required, скачать его сможет только администратор или любой пользователь Google соответственно.
  script: main.app
  
- url: .* # Страница, на которой размещена информация обо всех загруженных файлах и форма для загрузки.
  script: main.app
  login: admin


Для хранения файлов, в App Engine есть специальное хранилище Blobstore, а для данных — Datastore (отмечу, что используется нереляционная модель хранения данных; для доступа к данным имеется собственный фреймворк, похожий на ORM из Django). В нашем приложении каждому файлу присвоен объект FileRecord в Datastore со встроенным уникальным целочисленным идентификатором (имеется в виду извлечение ключа объекта по id), содержащий в себе объект BlobReferenceProperty:

class FileRecord(db.Model):
  blob = blobstore.BlobReferenceProperty()


Для более конкретного разбора URL и вообще, для инициализации приложения, в конце файла создается объект app, который является представителем WSGIApplication:

app = webapp2.WSGIApplication(
          [('/', MainHandler),
           ('/upload', UploadHandler),
           ('/delete/([^/]+)?', DeleteHandler),
           ('/get/([^/]+)?', GetHandler),
          ], debug=False)


В зависимости от полученного URL, приложение запускает соответствующий обработчик. В данном случае частично дублируются обработчики из app.yaml, поскольку в нем указаны настройки для авторизации.

Приложение работает следующим образом:
  • При обращении к ‘/’ проводится авторизация пользователя, затем загружаются все записи и выводится форма для загрузки файла.
  • При обращении к ‘/upload’, которое происходит автоматически при загрузке файла, создается объкт в Blobstore, который связывается с объектом в Datastore, затем в случае успех производится переход на ‘/’.
  • При обращении к ‘/delete/’ из URL извлекается номер объекта, после чего тот удаляется.
  • При обращении к ‘/get/’ из URL также извлекается номер объекта, после чего файл связанный с ним отправляется на загрузку.


Для проверки работы приложения, запустите отладочный сервер в консоли:

python google_appengine/dev_appserver.py <папка с вашим приложением>

Если хотите, чтобы сервер был доступен по сети, добавьте параметр --address 0.0.0.0 после dev_appserver.py.

Если никаких проблем не возникло, можно приступить к загрузке приложения:

python google_appengine/appcfg.py update <папка с вашим приложением>

Далее у вас спросят логин и пароль к учетной записи в Google, после чего приложение загрузится и будет доступно по адресу appid.appspot.com, где appid — это ваш уникальный идентификатор.
Исходные коды приложения на code.google.com (для использования достаточно просто отредактировать файл app.yaml): http://code.google.com/p/fileshare-appengine

Преимущества перед существующими файлообменниками:
  • Не нужно смотреть рекламу, ждать минуту и т.п.
  • Файл можно скачать по прямой ссылке.
  • Очень высокая надежность.
  • Только вы управляете тем, что залито на ваш файлообменник. Его не закроют из-за другого пользователя
  • В указанных пределах использования ресурсов все бесплатно.
  • Если даже выделенных ресурсов мало, можно сделать еще одно приложение (это уже личные домыслы, в правилах конкретно на этот случай ничего нету, так что лучшу поузнавать. UPDATE: правилами это все-таки запрещено).
Непонятки:
  • Не совсем ясно насчет максимального размера файла — в русской документации написано 2 Гб, а в английской — 32 Мб.
  • Выяснилось, что русские имена файлов не поддерживаются. Пока не совсем понял, почему.


UPDATE: Если на этапе загрузки скрипта в Windows выдается ошибка вида UnicodeDecodeError, возможно поможет удаление всех ключей, в которых есть кириллица, из ветки реестра HKEY_CLASSES_ROOT/Mime/Database/ContentType
UPDATE2: Теперь исходники можно просмотреть по ссылке http://code.google.com/p/fileshare-appengine/source/browse/
UPDATE3: а также на GitHub https://github.com/SergeyBurma/fileshare-appengine
Поделиться публикацией

Похожие публикации

Комментарии 44

    +2
    > Вариантов дальнейших действий два — или найти новый, пока еще не раскрученный файлообменник и использовать его, пока он не испортится, или организовать собственное решение
    Вобще есть еще третий — платить за использование сервиса. Странно, что вы о нем забыли.
      +1
      Забыл не это, забыл указать, что рассматриваются только бесплатные варианты.
        0
        Раз рассматриваются только бесплатные варианты, то нет смысла хранить файл объемом более 1Гб, если есть ограничение на 1 Гб входящего и 1 Гб исходящего трафика в день.
          +2
          Ну, можно по извращаться. Разбить файл на куски и заливать его частями каждый день. Думаю некоторые месье оценят.
            0
            Кроме месье есть еще госпожа автоматизация, а в моем рецепте для нее полная свобода:)
              +1
              Особенно если кто-то сделает автоматическое распределение загружаемых файлов по разным аккаунтам, тогда…
          0
          Посмотрите Google Cloud Storage — 5Gb места (превышение по $0,13) + 25Gb траффика включено. Не надо создавать сложностей с запуском приложения, особенно если учесть что при превышении квоты на GAE Вам придется переходить на платный тариф который начинается от $10/мес.
            0
            Если я правильно понимаю, там не бесплатный сервис, а пробный период до 30 июня?
              0
              Да, только сейчас заметил. Они ограничили срок бесплатного использования до 30 июня 2012. Но в любом случае 5*0,14=$0,70 в месяц — это одна поездка на автобусе или 15 СМС друзьям ;)
        +3
        Шикарно. В идеале — можно сделать api + декстопное приложение и сделать такой себе клон дропбокса.
        +3
        Amazon S3 в сравнении с GAE как-то поприятнее выглядит, имхо.
          0
          Признаюсь, не пробовал, а с GAE год дружу, из плюшек например API для обработки изображений, плюс недавно ввели возможность работать с объектами Blobstore как с обычными файлами. Ну и python люблю страшно:)
          У Amazone S3 вроде квоты меньше?
            0
            То есть.это все бесплатно и задаром?:) 5Гб-не очень много, правда.
            Скорее, тут больше подойдет выполнение простеньких скриптов, чем хостинг.
              0
              Учитывая, что основное назначение платформы совершенно отличается от предложенного в статье, 5Гб для файлов и 1Гб для базы данных — нормально, как для бесплатного хостинга.
              На одной конференции предлагалось использовать GAE для стартапов — учитывая, что платформа облачная, можно довольно дешево провести стресс-тесты, а бесплатных возможностей вполне достаточно на первое время.
                0
                да, только переползти после того, как стартап взлетел, на другую платформу сложновато
                  0
                  А зачем? Учитывая качество сервиса, цены там довольно оправданные и демократичные. Плюс, если использовать, к примеру, пропатченный специально под GAE Django или другой универсальный фреймворк, потом можно перенести данные на другую платформу.
                    +1
                    Качество сервиса весьма посредственное (доступность порядка 99%, под нагрузками очень сильно падает, время ответа — порядка 100-200мс, меньше не делается). Переход на собственный Django-сервер (с соответствующими программистами/администраторами) — удовольствие тоже весьма странное.
                    Amazon EC2 с возможностью масштабировать свои виртуальные машины с LAMP и доступностью по SLA 99,99% выглядит намного более привлекательно во всех смыслах.
                      +1
                      У меня такое ощущение, что мы пользуемся разными App Engine. Ни проблем с доступностью бесплатных приложений на HRDS, ни проседаний замечено не было. Запросы по 40-50мс — обычное дело. Конечно, пинг до Штатов великоват, но в веб-приложениях это не критично. Бесплатный GAE предоставляет сервис лучше чем большинство (если не все) отечественных платных хостингов. А встроенные инструменты разработчика позволяют экономить множество времени и сил при разработке и запуске приложения.

                      SLA Amazon EC2 несколько меньше — 99,95% в год. И это не гарантия того, что сервис будет работать бесперебойно. Если EC2 будет лежать больше 4 часов 23 минут, то можно получить обратно 10% от оплаты. У платных приложений App Engine SLA 99,95% в месяц. То есть 10% можно требовать уже после 22 минут отключки.
                      0
                      Скоро можно будет использовать на GAE обычную Джангу в связке с Google Cloud SQL.
                        0
                        Уже можно, но CloudSQL — early preview и только по персональному запросу. В реальности это стоит хороших денег, по сравнению с Datastore, не имеет мета-запросов (либо они отсутствуют сейчас в документации) так что если вы забыли когда-нибудь имя таблицы — узнать о её существовании будет нелегко.
                        Для джанги в 1.6.2 и 1.6.3 SDK уже включен db.backend для CloudSQL, но тот же South отказывается работать, а у меня руки до патча так и не дошли.
              0
              Там 15 гигабайт в месяц всего суммарного трафика.
                0
                Вообще то это абсолютно разные вещи. S3 можно сравнивать только с Google Cloud Storage, но у каждого есть свои плюсы и минусы. Под S3 уже много приложений, некоторые позволяют его подключать как диск в Linux. Зато в GCS есть бесплатное место и траффик (в S3 бесплатные квоты только в первый год пользования сервисом).
                0
                >Только вы управляете тем, что залито на ваш файлообменник. Его не закроют из-за другого пользователя

                Также интересны ограничения на хранимые в GAE данные. Не думаю, что вы хотите лишиться акаунта за нарушающие чьи-нибудь права файлы.
                  0
                  >Также интересны ограничения на хранимые в GAE данные.
                  ИМХО, никто эти данные проверять не будет, поскольку видеть полный список файлов может только администратор приложения, если конечно он себя как-нибудь не скомпрометирует. Если я правильно понял правила, даже если придет абуза, можно в течении какого-то срока все исправить и тогда приложение не закроют.
                  +1
                  Неплохо, но интереснее было бы сделать что-то с WebDAV — удобнее для пользования
                  0
                  Для чего понадобилась модель FileRecord? В BlobInfo информации более, чем достаточно для такого сервиса.
                    0
                    Модель FileRecord понадобилась для возможности обращения к конкретному BlobInfo по числовому id, а не длиннющему ключу. Если есть идеи, как это исправить — предлагайте.
                      0
                      Если принципиально использование id, то без этого, похоже, не обойтись. Однако, я делал приложения для хранения мелких файлов и брал за основу имя файла — возможности BlobInfo использовались более полно и получилась некоторая поддержка версий.

                      Пример выдачи файла с указанными именем и MD5:
                      class ServeFile(BlobstoreDownloadHandler): #webapp route: (r'/([a-hA-H0-9]{32})/.*', ServeFile),
                        def get(self, md5):
                          result = blobstore.BlobInfo.all().filter('md5_hash =', md5).get()
                          if not result: return self.error(404)
                          self.send_blob(result.key())


                      Пример выдачи последнего загруженного файла с указанным именем:
                      class ServeLatest(BlobstoreDownloadHandler): #webapp route: (r'/(.*)', ServeLatest),
                        def get(self, filename):
                          result = blobstore.BlobInfo.all().filter('filename =', filename).order('-creation').get()
                          if not result: return self.error(404)
                          self.send_blob(result.key())


                      Для работы второго примера в index.yaml необходимо прописать
                      indexes:
                      - kind: __BlobInfo__
                        properties:
                        - name: filename
                        - name: creation
                          direction: desc
                      


                      В целом, BlobInfo во многом похож на обычный db.Model, и в исходниках есть неплохая документация к нему.

                      Для разработки под GAE хочу посоветовать PyCharm. Эта IDE поддерживает App Engine из коробки, существенно повышает скорость и удобство и позволяет удобно читать исходники SDK.
                        0
                        >Если принципиально использование id, то без этого, похоже, не обойтись.
                        Сначала пробовал делать как вы, но в итоге понял, что скидывать другому человеку короткие цифровые ссылки все-таки лучше, чем длинные с закодированными кракозябрами.

                        >Для разработки под GAE хочу посоветовать PyCharm.
                        Давно советуют ее, но все никак не доберусь, пока что работаю в Eclipse и Geany.
                          0
                          я бы еще посоветовал buildout recipe.
                          Зачем вам md5 хеш который по сути требует cputime на расчет, и не сильно короче оригинального BlobKey?

                          Так же следовало сказать, что GAE SDK самостоятельно обновляет index.yaml соответствующим индексом, при обработке запроса, требующего индексы на свойствах условий.

                          Плюс — взгляните на ndb — весьма интересная альтернатива голому Datastore
                            0
                            Хеш рассчитывается один раз при загрузке файла, независимо от того, будут им пользоваться или нет. Потом он только берется из хранилища данных. Я его использую потому, что он характеризует файл, в отличие от BlobKey.

                            Там есть и другие полезные свойства, которые можно использовать:
                            #  Properties:
                            #    content_type: Content type of blob.
                            #    creation: Creation date of blob, when it was uploaded.
                            #    filename: Filename user selected from their machine.
                            #    size: Size of uncompressed blob.
                            #    md5_hash: The md5 hash value of the uploaded blob.
                              0
                              Всё кроме creation (created_at более pythonic) и md5 есть в BlobInfo, который всё равно нужен, чтобы раздать файл.

                              А md5 — ну разве что для сверки на стороне пользователя.
                                0
                                И creation, и md5 есть в BlobInfo. А в моем комментарии приведена часть документации к нему.
                                  0
                                  Ага, тупанул что-то. Видимо конец рабочего дня сказывается.
                      0
                      Статья клевая, все в ней ок, но у меня только один вопрос зачем вам code.google.com чтобы выложить туда архив с исходниками?

                      Мне просто не видится воозможным отрыть его и посмотреть с планшета, при том что в гугло коде есть возможность онлайн посмотреть исходники
                        0
                        Прошу прощения, исправился. Теперь вы можете просмотреть исходники по адресу http://code.google.com/p/fileshare-appengine/source/browse/
                          0
                          Большое человеческое спасибо!
                            0
                            и перепубликуйте исходный код на github, пожалуйста — мне было бы интересно на него посмотреть/форкнуть/переписать по своему видению, например (: а google-code не очень социален для разработки.
                        0
                        Кстати — с русскими именами файлов проблем не обнаружено, правда я отдачу файла/чтение из blobstore писал руками, т.к. использую не webapp2 а Flask. Ограничивая размер чанка до 1кб, т.к. память у инстанса не резиновая, вот моя реализация для Flask: gist.github.com/1789623

                        Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                        Самое читаемое