инструкция от lead back-end разработчика
Как оптимизировать работу с картинками на сайте или в приложении?
Если вам кажется, что загрузить картинку на сайт – это пара пустяков, наша статья для вас. Мы расскажем, почему длина пути от добавления изображения в админку до вывода его на экран зависит от того, какое качество вы хотите видеть в результате.
Возможно, вам случалось сталкиваться с такими косяками:
Знайте, там пошли по наименьшему пути сопротивления и не уделили должного внимания способам загрузки изображения на сайт или в приложении. Поэтому расскажем, как наша команда работает с картинкам в проектах.
- долгая загрузка картинки
- на айфоне плохое качество изображения
- на фотографии обрезало кому-то голову
- баннер на десктопе красивый, а не телефоне не видно контактных данных
Знайте, там пошли по наименьшему пути сопротивления и не уделили должного внимания способам загрузки изображения на сайт или в приложении. Поэтому расскажем, как наша команда работает с картинкам в проектах.
Дисклеймер: статья написана в феврале 2022 года. Использованы актуальные инструменты для этого времени.
Где хранить изображения для сайта?
Для хранения картинок мы используем S3. Это такой протокол для работы с файлами, который раньше ассоциировался только с сервисами Amazon, но сейчас его можно развернуть самому, или использовать много готовых решений. Например, в том же Яндексе. Фишка S3 хранилищ, что они “резиновые” и условно бесконечные. Если в приложении постоянно добавляются новые картинки, а вы не представляете, сколько их будет, и не хотите следить за свободным местом на диске, постоянно масштабировать его, то хранилище S3 – пока лучшее решение. Вы платите только за то, сколько места используете.
Кстати, особенно актуальна эта проблема для сервисов и e-comm. В нашей практике случалось, что ночью обновлялся каталог, и появлялись десятки гигабайт фотографий, а место на сервере заканчивалось. Используя S3, можно застраховать себя от этого.
Кстати, особенно актуальна эта проблема для сервисов и e-comm. В нашей практике случалось, что ночью обновлялся каталог, и появлялись десятки гигабайт фотографий, а место на сервере заканчивалось. Используя S3, можно застраховать себя от этого.
Как ресайзить картинки для веба или приложений?
Для работы с картинками мы используем https://imgproxy.net. Он у нас развернут как отдельный микросервис и присутствует в каждом проекте. Ранее мы делали ресайз картинок на PHP, так как это наш основной стек, потом перешли на node.js, но по итогу решили, что https://imgproxy.net – более гибкий вариант, написанный на языке Go, с кучей настроек и высокой скоростью работы. Что еще круто, есть не только ресайз, но и водяные знаки, центрирование и много чего другого – смотрите на сайте.
Как подготовить картинку для веба?
В вебе мы используем 2 формата картинок: webp и jpg. Webp на данный момент – наиболее современный из стабильных форматов, который поддерживают почти все браузеры. Он предполагает хорошее качество и небольшой вес. Для тех, кто не поддерживает webp, грузим jpg.
Еще есть разные разрешения. Мы ориентируемся на 3 контрольных размера: мобайл, десктоп и планшет. Правильнее для каждого разрешения использовать свою картинку, чтобы телефон получал нужный для него размер и подгружал ровно то, что требуется ему, а десктоп – себе. Бывает же такое, что на одном устройстве картинка 300 на 400, а на другом – 100 на 100.
Помимо этого есть такой показатель как плотность точек на экране. Например, вы слышали про retina экраны в Apple. Так вот сейчас плотность пикселей на один физический пиксель бывает и 2, и 3. Это можно проверить, например, здесь. Выходит, что нам требуется дополнительно делать подходящие для каждого экрана картинки со своей плотностью.
В итоге мы получаем целое комбо изображений, которое формируется в зависимости от браузера, разрешения, плотности экрана. Например, есть карточка товара в каталоге. Для одной такой позиции мы генерируем 3 разрешения (десктоп, планшет, мобайл), 2 формата (jpg и webp) и подходящие по плотности экрана 1X, 2X, 3X картинки.
Еще есть разные разрешения. Мы ориентируемся на 3 контрольных размера: мобайл, десктоп и планшет. Правильнее для каждого разрешения использовать свою картинку, чтобы телефон получал нужный для него размер и подгружал ровно то, что требуется ему, а десктоп – себе. Бывает же такое, что на одном устройстве картинка 300 на 400, а на другом – 100 на 100.
Помимо этого есть такой показатель как плотность точек на экране. Например, вы слышали про retina экраны в Apple. Так вот сейчас плотность пикселей на один физический пиксель бывает и 2, и 3. Это можно проверить, например, здесь. Выходит, что нам требуется дополнительно делать подходящие для каждого экрана картинки со своей плотностью.
В итоге мы получаем целое комбо изображений, которое формируется в зависимости от браузера, разрешения, плотности экрана. Например, есть карточка товара в каталоге. Для одной такой позиции мы генерируем 3 разрешения (десктоп, планшет, мобайл), 2 формата (jpg и webp) и подходящие по плотности экрана 1X, 2X, 3X картинки.
Фронтенд составляет требования для картинок таким образом.
А на бэкенде мы формируем целый массив картинок.
Как результат 1 загруженная картинка на примере каталога на выходе имеет 48 вариантов картинок различных размеров, разрешений и форматов.
Вот как выглядит компонент на react.
Как результат 1 загруженная картинка на примере каталога на выходе имеет 48 вариантов картинок различных размеров, разрешений и форматов.
Вот как выглядит компонент на react.
const vars = {
breakpoints: {
mobile: 500,
tablet: 1024,
},
}
export default function Picture({ desktop, tablet, mobile }) {
const desktopImages = desktop || {}
const {
x1: desktop_x1,
x2: desktop_x2,
webp_x1: desktop_webp_x1,
webp_x2: desktop_webp_x2, } = desktopImages
const tabletImages = tablet || {}
const {
x1: tablet_x1,
x2: tablet_x2,
webp_x1: tablet_webp_x1,
webp_x2: tablet_webp_x2, } = tabletImages
const mobileImages = mobile || {}
const {
x1: mobile_x1,
x2: mobile_x2,
webp_x1: mobile_webp_x1,
webp_x2: mobile_webp_x2, } = mobileImages
return desktop_x1 && desktop_x1.endsWith('.svg') ? (
<img src={desktop_x1} alt="" />
) : (
<picture>
<source
type="image/webp"
media={`(min-width: ${vars.breakpoints.tablet + 1}px)`}
srcSet={`${desktop_webp_x1}, ${desktop_webp_x2} 2x`}
/>
<source
media={`(min-width: ${vars.breakpoints.tablet + 1}px)`}
srcSet={`${desktop_x1}, ${desktop_x2} 2x`}
/>
<source
type="image/webp"
media={`(min-width: ${vars.breakpoints.mobile + 1}px)`}
srcSet={`${tablet_webp_x1}, ${tablet_webp_x2} 2x`}
/>
<source
media={`(min-width: ${vars.breakpoints.mobile + 1}px)`}
srcSet={`${tablet_x1}, ${tablet_x2} 2x`}
/>
<source
type="image/webp"
media={`(max-width: ${vars.breakpoints.mobile}px)`}
srcSet={`${mobile_webp_x1}, ${mobile_webp_x2} 2x`}
/>
<source
media={`(max-width: ${vars.breakpoints.mobile}px)`}
srcSet={`${mobile_x1}, ${mobile_x2} 2x`}
/>
<img src={desktop_x1} srcSet={`${desktop_x2} 2x`} alt="" />
</picture>
)
}
Как подготовить картинку для мобайла?
Ситуация в целом очень схожа с вебом. Повторяться не будем. Но есть особенности с поддержкой форматов. Например, webp поддерживает ios>15 версии, android – с 11.
Так как приложения мы делаем только на react native, компонент со стороны react native выглядит следующим образом.
Так как приложения мы делаем только на react native, компонент со стороны react native выглядит следующим образом.
export default function PDImage({ urls, ...props }) {
const getUri = () => {
const PD = PixelRatio.get()
if (PD <= 1 ) return urls.x1
if (PD > 1 && PPI <= 2) return urls.x2
if (PD > 2) return urls.x3
}
return (
<Image
source={{
uri: getUri(),
}}
{...props}
/>
)
}
Какого качества должны быть картинки для веба и мобайла?
В imgproxy, который мы используем для ресайза картинок, можно определять процент качества от исходного. Здесь следует найти баланс, ведь уменьшая этот параметр, вы уменьшаете и объем, но, соответственно, снижаете степень детализации. А если поставить 100% качество, получается “тяжелое” изображение.
Опытным путем мы для себя выбрали качество для webp 85%, для jpg – 95%.
В imgproxy, который мы используем для ресайза картинок, можно определять процент качества от исходного. Здесь следует найти баланс, ведь уменьшая этот параметр, вы уменьшаете и объем, но, соответственно, снижаете степень детализации. А если поставить 100% качество, получается “тяжелое” изображение.
Опытным путем мы для себя выбрали качество для webp 85%, для jpg – 95%.
Как кэшировать изображения?
На выходе получается, что для одной картинки мы имеем десятки изображений разных форматов и разрешений. Где все их хранить? Один из способов – при загрузке или появлении картинки в системе создавать сразу все необходимые варианты и статично хранить в S3.
Но потом начали наблюдать ситуацию, при которой очень быстро увеличивается объем хранилища и усложняется его поддержка, если появляется дополнительный размер. Например, создается новая страница на сайте, где нужен другой размер картинки с товаром. В итоге приходится пройтись по всем старым файлам и создать плюс один размер, что долго.
Поэтому мы пришли к варианту, когда в момент запроса картинки создаются необходимые варианты и складываются в кэш. За счет скорости работы imgproxy это довольно быстрая операция. Такой кэш хранится 1 месяц. В итоге у нас в кэше только картинки, которые часто запрашиваются, поэтому они отдаются быстро. А к товарам, которые, например, никогда не открывались на сайте, даже не создаются такие варианты картинок.
Вот какой долгий путь проходит картинка, в простой на первый взгляд схеме, где вы загружаете изображение, а мы выводим его на сайте. Все это для оптимизации и хорошего качества.
Но потом начали наблюдать ситуацию, при которой очень быстро увеличивается объем хранилища и усложняется его поддержка, если появляется дополнительный размер. Например, создается новая страница на сайте, где нужен другой размер картинки с товаром. В итоге приходится пройтись по всем старым файлам и создать плюс один размер, что долго.
Поэтому мы пришли к варианту, когда в момент запроса картинки создаются необходимые варианты и складываются в кэш. За счет скорости работы imgproxy это довольно быстрая операция. Такой кэш хранится 1 месяц. В итоге у нас в кэше только картинки, которые часто запрашиваются, поэтому они отдаются быстро. А к товарам, которые, например, никогда не открывались на сайте, даже не создаются такие варианты картинок.
Вот какой долгий путь проходит картинка, в простой на первый взгляд схеме, где вы загружаете изображение, а мы выводим его на сайте. Все это для оптимизации и хорошего качества.