Работа с большими файлами и range-запросами

Обработка больших файлов в сервис-воркере требует особого подхода, так как стандартная стратегия кэширования “cacheFirst” или “networkFirst” может быть неэффективной при передаче больших ресурсов, таких как видео, архивы или ISO-образы. Библиотека Workbox предоставляет возможности для тонкой настройки работы с такими файлами, включая поддержку range-запросов, которые позволяют загружать только часть файла.


Range-запросы и их значение

HTTP range-запросы позволяют клиенту запрашивать только фрагмент ресурса, задавая заголовок Range с диапазоном байт. Это критично для:

  • Воспроизведения видео и аудио потоков без загрузки всего файла.
  • Параллельной загрузки больших файлов по частям.
  • Экономии трафика при повторном запросе частей ресурса.

Пример заголовка range-запроса:

Range: bytes=0-999

Сервис-воркер должен уметь корректно обрабатывать такие запросы, чтобы вернуть только запрошенный фрагмент файла с соответствующими заголовками (Content-Range, Accept-Ranges, Content-Length).


Кэширование больших файлов в Workbox

Workbox предоставляет модуль workbox-range-requests, который позволяет кэшировать не весь файл целиком, а отдельные диапазоны, обеспечивая эффективное использование памяти и диска. Основные концепции:

  1. RangeCache – кэш для хранения сегментов больших файлов.
  2. RangeRequestsPlugin – плагин, интегрируемый с workbox-strategies, который перехватывает range-запросы и проверяет наличие соответствующего сегмента в кэше.

Пример использования RangeRequestsPlugin с CacheFirst стратегией:

import { registerRoute } from 'workbox-routing';
import { CacheFirst } from 'workbox-strategies';
import { RangeRequestsPlugin } from 'workbox-range-requests';
import { ExpirationPlugin } from 'workbox-expiration';

registerRoute(
  ({ request }) => request.destination === 'video',
  new CacheFirst({
    cacheName: 'video-cache',
    plugins: [
      new RangeRequestsPlugin(),
      new ExpirationPlugin({ maxEntries: 20 })
    ]
  })
);

Ключевые моменты:

  • RangeRequestsPlugin автоматически разбивает запросы на сегменты и сохраняет их в кэше.
  • ExpirationPlugin позволяет контролировать количество сегментов и их срок хранения.
  • Можно комбинировать с другими стратегиями (NetworkFirst) для потокового контента.

Работа с потоковыми ответами

При работе с большими файлами важно использовать streaming API в сервис-воркере. Workbox поддерживает обработку потоков через fetch и позволяет передавать части файла напрямую клиенту:

self.addEventListener('fetch', event => {
  const range = event.request.headers.get('range');
  if (range) {
    event.respondWith(handleRangeRequest(event.request));
  }
});

async function handleRangeRequest(request) {
  const url = new URL(request.url);
  const response = await caches.match(url.pathname) || await fetch(request);

  const buffer = await response.arrayBuffer();
  const [start, end] = parseRangeHeader(request.headers.get('range'), buffer.byteLength);
  const slicedBuffer = buffer.slice(start, end + 1);

  return new Response(slicedBuffer, {
    status: 206,
    statusText: 'Partial Content',
    headers: [
      ['Content-Range', `bytes ${start}-${end}/${buffer.byteLength}`],
      ['Accept-Ranges', 'bytes'],
      ['Content-Length', slicedBuffer.byteLength]
    ]
  });
}

function parseRangeHeader(range, totalLength) {
  const match = /bytes=(\d*)-(\d*)/.exec(range);
  const start = match[1] ? parseInt(match[1]) : 0;
  const end = match[2] ? parseInt(match[2]) : totalLength - 1;
  return [start, end];
}

Особенности реализации:

  • Обязательно возвращать статус 206 Partial Content для частичных ответов.
  • Заголовок Content-Range должен отражать фактический диапазон и общий размер файла.
  • Для больших файлов использование arrayBuffer() может быть заменено на потоковое чтение через Response.body для минимизации потребления памяти.

Оптимизация кэширования больших файлов

  1. Дробление на сегменты: Разбивка больших файлов на фрагменты фиксированного размера (например, 1–5 МБ) позволяет эффективно кэшировать и повторно использовать части файла.
  2. Стратегия “NetworkFirst” с fallback на кэш: Для динамически изменяемого контента можно сначала пытаться получить актуальные сегменты с сети.
  3. Очистка устаревших сегментов: С помощью ExpirationPlugin или собственных механизмов удаления старых сегментов предотвращается переполнение кэша.
  4. Совмещение с индексным кэшом: Для каждого файла можно хранить метаданные сегментов (например, список закэшированных диапазонов) для быстрого определения, какие части уже доступны.

Совместимость с видео и аудио потоками

Workbox и range-запросы особенно актуальны для видео-плееров, которые поддерживают HTTP Live Streaming (HLS) и DASH. В таких случаях:

  • Каждый сегмент видео можно кэшировать отдельно.
  • Range-запросы гарантируют, что плеер получает только необходимые куски.
  • Использование CacheFirst + RangeRequestsPlugin снижает нагрузку на сеть и ускоряет повторное воспроизведение.

Выводы по технической реализации

  • Range-запросы являются обязательным инструментом при работе с файлами >50 МБ.
  • Workbox предоставляет высокоуровневые плагины для интеграции с range-запросами.
  • Кэширование сегментов требует контроля памяти и хранения метаданных.
  • Потоковая обработка и корректные HTTP-заголовки обеспечивают совместимость с клиентскими плеерами и ускоряют работу приложений.

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