Chunked Transfer — это механизм HTTP-протокола, при котором сервер передаёт тело ответа отдельными фрагментами (чанками), не указывая заранее общий размер данных. Каждый чанк содержит свой размер в шестнадцатеричном формате и данные указанной длины, а завершается финальным чанком нулевого размера. По данным MDN Web Docs, 2025, Transfer-Encoding: chunked включается сервером автоматически, когда размер ответа неизвестен заранее — например, при генерации контента на лету или потоковой передаче данных.
Главное
Chunked Transfer — это механизм HTTP, определённый в спецификации HTTP/1.1 (RFC 7230, раздел 4.1), который позволяет серверу отправлять тело ответа по частям без указания общего Content-Length. Вместо того чтобы вычислять размер ответа перед отправкой, сервер начинает передачу немедленно, отправляя данные фрагментами по мере их готовности. Каждый фрагмент сопровождается собственным заголовком размера, что позволяет клиенту собирать ответ из кусочков.
Механизм включается заголовком Transfer-Encoding: chunked. Когда клиент видит этот заголовок в ответе, он знает, что тело будет передаваться чанками, и должен читать ответ в цикле: прочитать размер чанка, затем прочитать данные указанного размера, затем повторить. Процесс завершается, когда встречается чанк нулевого размера. Chunked Transfer — обязательная часть HTTP/1.1, поддерживаемая всеми современными веб-серверами и HTTP-клиентами.
Главная причина использования chunked transfer — динамическая генерация контента. Когда сервер генерирует ответ на основе запроса к базе данных, внешнего API или длительного вычисления, он не может заранее знать размер результата. Вместо того чтобы буферизовать весь ответ в памяти (что рискованно для больших объёмов), сервер включает Transfer-Encoding: chunked и отправляет данные по мере готовности. Это особенно важно для серверов с ограниченной памятью и для ответов, размер которых может быть очень большим — от 100 МБ и выше.
В HTTP/2 механизм chunked transfer как таковой отсутствует, поскольку протокол использует мультиплексирование потоков на уровне фреймов. В HTTP/2 данные любого размера передаются в фреймах DATA, и размер тела ответа не нужно объявлять заранее — поток может быть закрыт в любой момент. Современные серверы автоматически преобразуют chunked-ответы HTTP/1.1 в эквивалентную потоковую передачу при проксировании на HTTP/2 upstream. Chunked Transfer остаётся актуальным для HTTP/1.1-соединений.
Когда сервер решает использовать Chunked Transfer, он не вычисляет Content-Length, а отправляет заголовок Transfer-Encoding: chunked. Далее тело ответа формируется как последовательность чанков. Каждый чанк начинается со строки, содержащей размер чанка в шестнадцатеричном формате (без префикса 0x), за которой следует CRLF (\r\n). Затем идут данные чанка указанного размера, завершающиеся CRLF. Последний чанк имеет размер 0, после которого могут следовать trailer-заголовки.
Шестнадцатеричный размер позволяет передавать чанки любого размера от 1 байта до теоретически неограниченного объёма. На практике размер чанка выбирается сервером: типичные значения — 4 КБ, 8 КБ или 16 КБ. Оптимальный размер чанка — кратным размеру TCP-сегмента (обычно 1460 байт для Ethernet), чтобы минимизировать фрагментацию на транспортном уровне. Nginx использует чанки по 4 КБ по умолчанию, Apache — по 8 КБ.
Клиент, получивший Transfer-Encoding: chunked, обязан читать ответ чанк за чанком до завершающего нулевого чанка. Если клиент не поддерживает chunked transfer, сервер не может использовать этот режим. На практике все современные HTTP-клиенты — браузеры, OkHttp, URLSession, curl — полностью поддерживают chunked-ответы. Потоковое чтение позволяет клиенту начать обработку данных до получения полного ответа, что критически важно для производительности.
| Элемент чанка | Формат | Пример |
|---|---|---|
| Размер чанка | HEX + CRLF | 1000\r\n |
| Данные чанка | [размер байт] + CRLF | [4096 байт данных]\r\n |
| Завершающий чанк | 0\r\n | 0\r\n |
| Trailer (опционально) | Заголовки + CRLF | Expires: Wed, 21 Oct 2025\r\n |
Chunked Transfer поддерживает trailer-заголовки — дополнительные HTTP-заголовки, которые передаются после последнего чанка. Это полезно для метаданных, которые становятся известны только после завершения генерации ответа: например, Content-MD5 или X-Compression-Ratio. Trailer-заголовки должны быть объявлены в заголовке Trailer: Content-MD5, X-Compression-Ratio. На практике trailer используются редко — большинство серверов не включает их в ответы.
Chunked-ответ имеет строго определённую структуру, которую клиент обязан корректно разобрать. Рассмотрим полный пример HTTP-ответа с Transfer-Encoding: chunked. После заголовков и пустой строки начинается тело ответа. Структура тела представляет собой последовательность: размер_чанка\r\nданные\r\nразмер_чанка\r\nданные\r\n... до 0\r\n\r\n. Каждый размер передаётся в шестнадцатеричной системе счисления ASCII-символами.
Пример ответа сервера с Chunked Transfer:
HTTP/1.1 200 OK
Content-Type: text/plain
Transfer-Encoding: chunked
7\r\n
Hello \r\n
6\r\n
World!\r\n
0\r\n
\r\n
В этом примере сервер передаёт строку "Hello World!" двумя чанками. Первый чанк размером 7 байт содержит "Hello ", второй — 6 байт "World!". Клиент собирает данные из обоих чанков и получает полную строку. Важно: размер чанка включает только данные, не включая CRLF-разделители самих чанков. Завершающий пустой чанк (0\r\n) оповещает клиента об окончании передачи.
import java.net.HttpURLConnection
import java.io.BufferedReader
import java.io.InputStreamReader
fun readChunkedResponse() {
val url = java.net.URL("https://stream.example.com/data")
val connection = url.openConnection() as HttpURLConnection
val reader = BufferedReader(
InputStreamReader(connection.inputStream)
)
var line: String?
while (reader.readLine().also { line = it } != null) {
println("Chunk: $line")
}
reader.close()
}
OkHttp полностью абстрагирует разработчика от деталей Chunked Transfer. При получении ответа с Transfer-Encoding: chunked OkHttp автоматически собирает чанки и предоставляет разработчику полное тело ответа через response.body?.string(). Для потоковой обработки используется response.body?.source(), который возвращает BufferedSource и позволяет читать данные по мере поступления. Разработчику не нужно вручную разбирать hex-размеры и CRLF — библиотека делает это автоматически.
Content-Length и Transfer-Encoding: chunked — два взаимоисключающих способа указать размер тела HTTP-сообщения. Content-Length — это заголовок, который содержит точный размер тела в байтах. Он обязателен для ответов, размер которых известен заранее, и для запросов с телом (POST, PUT). Content-Length позволяет клиенту заранее выделить буфер нужного размера и проверить, что получены все данные.
Chunked Transfer используется, когда размер тела заранее неизвестен. Это происходит в трёх основных сценариях: динамическая генерация контента (например, запрос к БД, результат которого ещё не получен), потоковая передача больших файлов (чтобы не буферизовать весь файл в памяти) и Server-Sent Events (SSE) для передачи событий в реальном времени. Выбор между Content-Length и chunked — ответственность сервера. Если сервер знает размер до начала передачи, он должен использовать Content-Length как более простой и предсказуемый механизм.
Спецификация HTTP/1.1 запрещает одновременное использование Content-Length и Transfer-Encoding: chunked. Если сервер отправляет оба заголовка, клиент должен игнорировать Content-Length и обрабатывать ответ как chunked. Приоритет Transfer-Encoding над Content-Length установлен в RFC 7230 для случаев, когда proxy-сервер модифицирует тело ответа и не может сохранить исходный Content-Length. Некоторые старые HTTP-клиенты некорректно обрабатывают эту ситуацию, но современные реализации следуют спецификации.
Существуют сценарии, в которых Content-Length принципиально не может быть рассчитан заранее. Динамические отчёты, генерируемые по запросу пользователя с фильтрацией и агрегацией — сервер не знает объём данных до завершения запроса к БД. Потоковое видео, передаваемое с камеры в реальном времени — размер бесконечен. SSE и long polling для уведомлений — ответ может длиться неопределённо долго. Во всех этих случаях Chunked Transfer — единственный корректный механизм.
Chunked Transfer лежит в основе многих потоковых технологий в вебе. Самая известная — Server-Sent Events (SSE), где сервер отправляет события клиенту через одно HTTP-соединение с Transfer-Encoding: chunked. SSE использует специальный текстовый формат (data: сообщение\n\n), но транспортный уровень — обычный chunked transfer. Браузер получает события по мере их отправки сервером, не дожидаясь завершения ответа.
Streaming аудио и видео также опирается на Chunked Transfer. Медиа-серверы, такие как Nginx RTMP и Wowza Streaming Engine, отправляют медиаданные чанками через HTTP. Плеер на стороне клиента начинает воспроизведение, когда получен первый чанк, не дожидаясь полной загрузки файла. Это сокращает время до начала воспроизведения (Time to First Frame) с десятков секунд до 1-2 секунд. YouTube и Netflix используют именно этот подход для своих HTTP-потоков.
В мобильной разработке Chunked Transfer используется для передачи больших объёмов данных без загрузки всего ответа в память. При загрузке изображений через Coil или Glide на Android библиотеки читают потоковые данные чанками и постепенно декодируют изображение. Это позволяет отображать большие изображения (10+ МБ) без OutOfMemoryError. OkHttp поддерживает потоковое чтение через response.body?.byteStream(), который возвращает InputStream, читающий данные чанк за чанком.
gRPC использует HTTP/2, где потоковая передача встроена на уровне протокола и не требует отдельного механизма chunked. GraphQL-серверы, работающие через HTTP/1.1, могут использовать Chunked Transfer для потоковой передачи результатов подписок (subscriptions). Apollo Server и Hasura отправляют chunked-ответы для GraphQL-подписок, передавая события по мере их возникновения. Клиент получает обновления в реальном времени без необходимости поллинга.
Chunked Transfer предоставляет важные преимущества для веб-приложений. Немедленная отправка данных — сервер не буферизует ответ перед отправкой, снижая задержку (latency) до первого байта. Потоковая обработка — клиент может начать обрабатывать данные по мере поступления, не дожидаясь полной загрузки. Отсутствие ограничений по памяти — сервер не хранит полный ответ в памяти, что критично для больших объёмов данных. Возможность передачи бесконечных потоков — SSE, live-видео, мониторинг.
Однако у Chunked Transfer есть ограничения. Накладные расходы на каждый чанк составляют 6-12 байт на размер + CRLF, что для большого количества мелких чанков (например, по 100 байт) может увеличить размер ответа на 10-15%. Невозможность указать точный размер — клиент не может заранее выделить буфер или показать прогресс-бар. Проблемы с прокси-серверами — некоторые старые прокси не поддерживают chunked transfer и не могут кэшировать такие ответы. Отсутствие поддержки возобновления загрузки — для частично полученных chunked-ответов нельзя сделать Range-запрос.
По данным HTTP Archive, 2025, около 35% всех HTTP-ответов используют Transfer-Encoding: chunked. Среди них преобладают динамические страницы (60%), API-ответы (25%) и медиапотоки (15%). Статические файлы практически всегда используют Content-Length, так как их размер известен заранее. Доля chunked-ответов постепенно снижается с распространением HTTP/2, где потоковая передача реализована на уровне фреймов без необходимости в дополнительном заголовке Transfer-Encoding.
В мобильной разработке используйте Chunked Transfer для загрузки больших файлов (изображения, видео) и для API-запросов, возвращающих большие массивы данных. OkHttp полностью поддерживает chunked transfer без дополнительной настройки. Для загрузки на сервер (upload) Chunked Transfer не применяется — для этого используется Transfer-Encoding не для upload в HTTP/1.1. На iOS URLSession поддерживает как отправку, так и получение chunked-данных без специальной конфигурации. Потоковый парсинг JSON (например, через Jackson Streaming API или Moshi) позволяет обрабатывать большие JSON-массивы по мере поступления chunked-потока.
Часто задаваемые вопросы
Сервер включает Chunked Transfer автоматически, когда размер ответа неизвестен. Nginx добавляет Transfer-Encoding: chunked, если не задан Content-Length. В Spring Boot StreamingResponseBody и SseEmitter автоматически используют chunked transfer. В Node.js Express ответ становится chunked, если вызвать res.write() и res.end() без Content-Length.
Нет, спецификация HTTP/1.1 запрещает одновременное использование Content-Length и Transfer-Encoding: chunked. Если сервер отправляет оба заголовка, клиент должен игнорировать Content-Length и обрабатывать ответ как chunked. Это правило установлено в RFC 7230 для совместимости с прокси-серверами, которые могут изменять тело ответа.
Оптимальный размер чанка зависит от сценария. Для обычных веб-страниц — 4-8 КБ. Для потоковой передачи видео — 16-64 КБ. Для SSE — минимальные чанки по 1-2 КБ для снижения задержки. Размер чанка должен быть кратным размеру TCP-сегмента (1460 байт для Ethernet) для минимизации фрагментации на транспортном уровне.
Современные прокси-серверы (Nginx, HAProxy, Envoy) поддерживают Chunked Transfer. Прокси может передавать чанки дальше без буферизации (streaming), либо буферизовать весь ответ и переотправить с Content-Length. Старые прокси могут буферизовать chunked-ответ до завершения, что увеличивает задержку. HTTP/2 решает эту проблему на уровне протокола.
Это одно и то же. Chunked Transfer — это полное название механизма из спецификации HTTP/1.1. HTTP chunked encoding — то же самое, иногда используется в документации библиотек. Transfer-Encoding: chunked — заголовок, который включает этот режим. Все три термина описывают один и тот же механизм передачи данных частями.
Итоги
Мы разработаем мобильное приложение под ключ
IT Sectr создаёт приложения для iOS и Android для стартапов и бизнеса с 2017 года. Мы проконсультируем вас и предложим наилучшее решение.
Читайте также