В составе команды разработчиков iOS я принимал участие в разработке сервиса, который позволяет пользователю загружать фотографии и делиться ими с другими пользователями сервиса в различных сферах человеческой деятельности с меньшими затратами ресурсов.

Для изображений пользователь должен открыть настраиваемую камеру и сделать несколько снимков. Также пользователь может открыть свою библиотеку и выбрать из нее некоторые изображения. Нам нужно было хранить много изображений на сервере и предоставлять к ним быстрый доступ. Мы решили загружать изображения в Amazon S3, получать URL-адреса изображений и работать с ними. Поэтому мы использовали описанный алгоритм получения, загрузки и выгрузки изображений, чтобы достичь этой цели.

Начиная с iOS 9, разработчикам следует использовать фреймворк Photos. Этот фреймворк имеет значительное преимущество перед фреймворком AssetsLibrary, но в то же время имеет недостатки. Основная проблема, с которой мы столкнулись с этим фреймворком, заключалась в выборе фотографии из библиотеки. Когда вы выбираете изображение в первый раз, вам нужно загрузить полное изображение из iCloud, что занимает некоторое время, но фреймворк сразу выдает изображение предварительного просмотра небольшого размера (200 × 200).

Фреймворк Photos позволяет запускать дополнительную предварительную загрузку в кеш, поэтому мы решили использовать его для оптимизации этого процесса. Когда пользователь выбирает изображения в массиве, мы предварительно загружаем их, а когда пользователь заканчивает выбор, фотографии с исходным размером уже загружаются из облака.

После получения всех фотографий с устройства / камеры / облака мы получаем их фактический размер в мегабайтах, их количество и делаем запрос на сервер, чтобы узнать, достаточно ли у пользователя места для загрузки этих файлов относительно его плана. Если в корзине недостаточно места, сервер возвращает ошибку или создает объекты Photo с идентификатором в базе данных и датой создания.

После получения объектов Photo мы начинаем загружать изображения на сервер.

Загрузка обрабатывается как фоновая задача, поэтому пользователю не нужно ждать загрузки и он может продолжить использование приложения.

Когда начинается загрузка, мы отображаем выбранные фотографии в списке фотографий пользователя, но со статусом «загрузка» и полосой состояния в процентах для каждой отображаемой фотографии. Когда загрузка завершена, мы меняем статус на «загружено», и пользователь может работать с загруженными файлами.

Что касается другого пользователя, который войдет в эту папку с другого устройства и будет иметь учетную запись, он увидит объекты Photo, но без изображений, потому что они еще не загружены в S3. Они будут иметь статус «в процессе»; это означает, что изображения загружаются и скоро будут доступны. Для MVP мы решили использовать только REST, без сокета. Итак, если у нас есть объекты Photo со статусом «в процессе», мы обновляем их с сервера по 10 или 30 секунд каждый (в зависимости от качества Интернета), в то время как все объекты будут иметь статус «загружены», и мы сможем отображать изображения.

Что касается загрузки изображений в S3, здесь также есть некоторая архитектурная магия:

После получения массива изображений с камеры мы создаем очередь загрузки изображений и добавляем ее в глобальный менеджер загрузки для выполнения.

Итак, у нас есть глобальный менеджер загрузки с массивом очередей. Каждая очередь имеет массив задач загрузки и идентификатор папки.

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

Очереди тоже совсем не такие простые. Каждая очередь имеет статус загрузки. Если приложение закрывается и завершается, то все активные очереди меняют статус на «пауза»; после повторной загрузки приложения приостановленные очереди возобновятся и продолжат загрузку изображений.

Мы решили загружать до пяти изображений за раз, чтобы не использовать много памяти и мощности процессора одновременно. Например, если пользователь начинает загружать 20 изображений, они будут разделены на четыре группы с пятью изображениями в очереди, и они будут загружены одно за другим.

После реализации загрузки в S3 изображений большого размера, у нас остается открытый вопрос об эскизах изображений, потому что загрузка изображений большого размера с сервера для отображения небольшого превью не имеет смысла. Загружать и большие, и маленькие изображения тоже не имеет смысла, потому что для одного изображения мы должны создать два запроса на загрузку в S3.

В результате глубокого исследования мы решили использовать Amazon Lambda, которая позволяет использовать исполняемый скрипт на Java, Node.js и Python. Когда мы загружаем изображение большого размера, Amazon запускает наш скрипт, создает уменьшенную копию изображения и сохраняет ее в другую папку корзины с именем статической структуры, например ‹imagename› _preview.jpeg. После создания и сохранения небольшой копии нашего изображения мы запускаем наш второй скрипт, который уведомляет наш сервер о том, что изображения были загружены и сохранены. После этого наш сервер меняет статус фотообъекта на загруженный, и пользователь может получать изображения из S3.
Наш скрипт был разработан на Node.js.

Также оптимизирована работа с изображениями на стороне клиента. Мы должны платить Amazon за каждую загрузку изображения, и, поскольку нам нужно два изображения каждой фотографии, это может добавить к большому количеству запросов к Amazon S3. Мы решили один раз загрузить образ на устройство, а затем сохранить его на устройстве в каталоге. Мы не храним изображения в БД, потому что одно изображение может иметь размер до восьми мегабайт, а БД будет расти и в конце концов станет слишком большой.

В результате, детально проработав архитектуру загрузки и выгрузки, мы минимизировали ресурсы, используемые на обеих сторонах (на устройстве и на нашем сервере), на DO и Amazon S3.

Стек технологий:

Мобильный (iOS):

  • Фреймворки iOS SDK
  • Пользовательские платформы (TPKeyboardAvoiding, Fabric, SVProgressHUD, AWSS3, MagicalRecord, AFNetworking, SDWebImage)

Сторона сервера:

  • Бэкэнд (Ruby on Rails, Redis, Sidekiq, Swagger, Docker)
  • База данных (PostgreSQL)
  • Хостинговая среда (Digital Ocean)
  • Файловое хранилище (Amazon S3)
  • Почтовик (Mandrill)

Внешний интерфейс:

  • JS - ES6, Angular 1.5, CSS - SASS, HTML, Webpack

____________

Первоначально опубликовано на masterofcode.com/blog

Master of Code предлагает полномасштабный дизайн и разработку интегрированных решений для Интернета, мобильных устройств и чат-ботов. Наш обширный опыт в различных сферах бизнеса, включая образование, медицину и электронную коммерцию, позволяет нам предлагать решение, подходящее для вашего бизнеса. Мы всегда здесь, чтобы помочь, если есть что-то, что мы можем сделать.