Разработчики приложений для загрузки фото с мероприятий получили конкретный чек-лист, позволяющий поддерживать загрузку из браузера даже в условиях переполненной площадки. Один пользователь может переключаться с Wi-Fi на сотовую сеть, в то время как десятки устройств борются за один и тот же хотспот. В руководстве показано, как предотвратить исчезновение фотографии после появления уведомления «загрузка завершена», даже если гость заблокирует телефон или произойдет кратковременный сбой в сети.
Почему обычная загрузка дает сбои на свадьбах и фестивалях
В офисе ноутбук подключен к стабильному Ethernet-каналу, и единственный пользователь просто нажимает «отправить». На свадебном приеме или музыкальном фестивале то же самое действие может вызвать каскад проблем: гость переходит из зала церемоний на парковку, роутер не справляется с сотнями телефонов или телефон теряет Wi-Fi и переключается на сотовую связь. Браузер мог уже передать каждый байт на сервер, но сервер еще не успел сохранить файл в хранилище. Если интерфейс объявляет об успехе в тот момент, когда индикатор прогресса достигает 100 %, гость может удалить фото, оставив организатора с отсутствующим файлом.
Скрытая цена подхода «просто загрузи»
Наивный подход рассматривает загрузку как один HTTP POST-запрос. Это работает при стабильном соединении, но в перегруженной сети каждое прерывание заставляет процесс начинаться заново для всего файла. Пользователи испытывают разочарование, а потребление трафика резко возрастает, когда десятки телефонов одновременно пытаются повторить попытку. Разделение файла на части (чанки) и отслеживание каждой части усложняет систему, но результатом становится предсказуемая передача с низкими накладными расходами, которая выдерживает переключения между сетями.
Создание системы возобновляемой поблочной загрузки
Ниже представлен практический пошаговый рецепт.
1. Генерируйте ID загрузки перед отправкой любых данных из браузера
Создайте универсальный уникальный идентификатор (UUID) локально и отправьте его на сервер в качестве первого запроса. Сервер регистрирует сессию под этим ID. Если позже браузер повторит попытку из-за тайм-аута, он включит тот же UUID, что позволит серверу распознать сессию и избежать дублирования записей. Это делает рабочий процесс идемпотентным — повторение того же запроса не приводит к нежелательным последствиям.
2. Разделите файл на блоки по 5–10 МБ
Размер блока — это компромисс. Маленькие блоки (менее 1 МБ) увеличивают количество HTTP-запросов и связанные с ними накладные расходы на заголовки. Слишком большие блоки делают любое прерывание дорогостоящим, так как клиенту придется заново отправлять огромный фрагмент. Для типичных фотографий и коротких видео оптимальным является баланс в 5–10 МБ: каждый запрос завершается достаточно быстро, чтобы интерфейс оставался отзывчивым, но при этом количество запросов остается управляемым.
3. Ограничьте количество параллельных загрузок
Мобильные браузеры могут открывать множество соединений, но в перегруженной сети Wi-Fi каждый дополнительный поток конкурирует за ограниченную пропускную способность. Два стабильных потока лучше, чем восемь конкурирующих. Используйте API navigator.connection, чтобы обнаруживать условия низкой пропускной способности и автоматически снижать параллелизм.
4. Сохраняйте состояние загрузки в IndexedDB
Храните ID загрузки, список уже отправленных блоков и любые подтвержденные сервером смещения (offsets) в IndexedDB браузера. Если страница перезагрузится или пользователь закроет вкладку, клиент сможет восстановить состояние при следующей загрузке. Когда пользователь снова откроет страницу, предложите ему выбрать тот же файл; сохраненные метаданные позволят возобновить загрузку с последнего подтвержденного блока, а не начинать всё сначала.
5. Отслеживайте реальные изменения сети, а не только navigator.onLine
Флаг navigator.onLine часто сообщает «online», даже когда соединение невозможно использовать. Вместо этого установите короткий тайм-аут запроса (например, 5 секунд) для каждого блока. Если происходит тайм-аут, считайте, что сеть недоступна. Когда связь восстановится, запросите у сервера список блоков, которые у него уже есть, и продолжайте загрузку только недостающих частей. Это позволит избежать отправки дублирующихся данных после кратковременного сбоя.
6. Используйте экспоненциальную задержку с джиттером для повторных попыток
Когда устройства многих гостей заметят, что сеть восстановилась, они могут одновременно начать повторные попытки, перегрузив сервер. Экспоненциальная задержка (exponential backoff) заставляет каждую последующую попытку ждать дольше предыдущей, а джиттер (jitter) добавляет небольшое случайное смещение. Такая комбинация распределяет трафик повторных попыток во времени на несколько секунд, предотвращая резкий скачок нагрузки.
7. Показывайте многоуровневую и доступную обратную связь
Трехуровневая статусная строка сообщает о реальном состоянии файла:
- Received (Получено) — сервер сохранил все блоки и пометил файл как завершенный.
- Preparing (Подготовка) — сервер создает миниатюры или выполняет транскодирование видео.
- Available (Доступно) — организатор может просмотреть или скачать файл.
Не полагайтесь исключительно на цвет; сочетайте иконки с коротким текстом, чтобы пользователи программ чтения с экрана также могли понимать прогресс.
Что всё ещё может пойти не так?
Даже грамотно спроектированная загрузка с возможностью возобновления может столкнуться с несколькими пограничными случаями.
На что обратить внимание в дальнейшем
Веб-платформа постоянно развивается.
Итог
Загрузка частями с возможностью возобновления, которая отслеживает каждый фрагмент, сохраняет состояние локально и интеллектуально выполняет повторные попытки, превращает нестабильную сеть в надежный канал для передачи фотографий гостей. Реализуйте приведенный выше контрольный список.
