October 25, 2017

Работа с изображениями в Teletype

by Nikolay Ivanushkin
Работа с изображениями в Teletype

Недавно Телетайп получил большое обновление. Мы переработали дизайн, добавили приятных фишек, вроде выключения ката, скрытия участников блога, цветовых схем и всякого look and feel. Внутри тоже произошли некоторые интересные изменения.

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

Вводные данные

Телетайп работает целиком на Node.js, и использует базу данных PostgreSQL. Все сервисы (кроме БД) мы оборачиваем в Docker-контейнеры, и балансируем через nginx. При этом сервисы общаются через Docker network.

Генерируем картинки для шаринга в социальных сетях

Телетайп поддерживает формат Open Graph. Нам очень хотелось, чтобы для каждой статьи и страницы блога генерировались красивые картинки для шаринга.

Поскольку мы используем JavaScript, нам нет никаких проблем сделать такие картинки обычной HTML версткой. Всё что нам нужно, просто отрендерить на стороне сервера и сохранить картинку в PNG. Это очень упрощает жизнь, т.к. нет проблем сверстать это. А рендерингом занять PhantomJS.

Весть сервис работы с картинками был вынесен из главного бекенда. По этому, когда статья обновляется, для нее на основном бекенде генерируется HTML, и отправляется на рендеринг в сервис работы с картинками. В ответ мы получаем уже адрес сохраненной картинки. Всё что нам нужно — просто обновить его в базе.

Загружаем картинки

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

Что можно бы еще оптимизировать?

Во первых мы не удаляем картинки с сервера. Это сложно, и не полезно для дисков, и влечет дополнительные расходы. Удобно же при редактировании поста просто удалить картинку, и не ждать, когда она удалиться.

Во вторых, вполне нормальный пользовательский кейс, когда вы добавили картинку в статью, удалили, передумали и снова добавили. Значит мы загрузим две одинаковые картинки? Бесплатно? Ну нет.

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

По этому, для картинки мы генерируем 2 хеш-суммы — SHA1 и SHA256. Это очень сильно про запас, но накладные расходы по сравнению с сохранением картинки на диск минимальны.

Теперь перед загрузкой картинки, мы просто на клиенте берем 2 хеш-суммы, и делаем проверку одним запросом. Если такая картинка уже загружена — сервер вернет нам ее адрес. Все картинки обезличиваются, и сохраняются с UUID в имени: https://teletype.in/files/be/bec30fa9-8338-4790-bc49-be1f1be49768.jpg, как-то так. На всякий случай, при загрузки картинки сервер сам сверит хеш-суммы с имеющимися в базе. Но только потому-что хеши в таблицах могут быть только уникальными.

Обрабатываем картинки

Не так давно, нам пожаловался пользователь. Он загрузил фотографии с телефона в статью, а они неправильно повернулись.

Всё дело в EXIF. Изначально картинка повернута как попало, а правильная ориентация выставляется из EXIF данных с параметром orientation. Всего типов ориентации картинки может быть 8.

Итого, нужно было повернуть картинку. Вырезать весь EXIF (и сэкономить немного памяти дискам), ну и в случае с аватарками привести их к нужным нам размерам.

Всё это можно было сделать с помощью ImageMagic. Но поскольку один из разработчиков Телетайпа большой любитель JS, да и человеко-понятность ImageMagic убывающе мала, мы нашли модуль для Node.js – lwip. Он умеет вполне достаточно: повернуть картинку, зеркально отразить, обрезать, поменять размеры, и сохранить всё с минимальным набором параметров. За одно при сохранении добавили дополнительную оптимизацию картинок (85% качества для JPEG мало кто заметит, ведь да?).

В следующих сериях

  1. Работа с изображениями
  2. Домены блогов. Как работает привязка
  3. Домены блогов. Как работают домены
  4. Как мы готовим Docker
  5. Изоморфный Телетайп без React и Virtual DOM
  6. Проблемы визуальных редакторов текста
Development