Chunked Transfer در توسعه وب — چیست، فرمت و اصل انتقال تکه‌ای

نویسنده: IT Sectr منتشر شده: 2026-03-10 زمان مطالعه: 9 دقیقه

Chunked Transfer — مکانیزمی از پروتکل HTTP است که در آن سرور بدنه پاسخ را به صورت تکه‌های جداگانه (chunk) ارسال می‌کند، بدون اینکه از قبل اندازه کل داده را مشخص کند. هر chunk شامل اندازه خود در قالب هگزادسیمال و داده‌های با طول مشخص شده است و با یک chunk نهایی با اندازه صفر پایان می‌یابد. طبق MDN Web Docs, 2025، Transfer-Encoding: chunked به طور خودکار توسط سرور فعال می‌شود زمانی که اندازه پاسخ از قبل مشخص نیست — مثلاً در تولید محتوای لحظه‌ای یا انتقال جریانی داده‌ها.

نکات اصلی

  • Chunked Transfer — ارسال پاسخ HTTP به صورت تکه‌ای بدون مشخص کردن Content-Length.
  • Transfer-Encoding: chunked — هدری که حالت انتقال تکه‌ای داده‌ها را فعال می‌کند.
  • هر chunk شامل اندازه به صورت hex، داده‌ها و CRLF پایانی است و پایان با chunk با اندازه صفر مشخص می‌شود.
  • Streaming — کاربرد اصلی chunked transfer برای انتقال صدا، ویدئو و رویدادهای SSE.
  • Chunked Transfer با هدر Content-Length ناسازگار است — همزمان استفاده نمی‌شوند.

Chunked Transfer چیست؟

Chunked Transfer مکانیزم HTTP است که در مشخصات HTTP/1.1 (RFC 7230، بخش 4.1) تعریف شده است و به سرور اجازه می‌دهد بدنه پاسخ را بدون مشخص کردن Content-Length کامل به صورت تکه‌ای ارسال کند. به جای محاسبه اندازه پاسخ قبل از ارسال، سرور بلافاصله انتقال را آغاز می‌کند و داده‌ها را با آماده شدنشان به صورت تکه‌ای می‌فرستد. هر تکه با هدر اندازه خود همراه است که به کلاینت امکان می‌دهد پاسخ را از تکه‌ها جمع‌آوری کند.

این مکانیزم با هدر Transfer-Encoding: chunked فعال می‌شود. وقتی کلاینت این هدر را در پاسخ می‌بیند، می‌داند که بدنه به صورت chunk ارسال می‌شود و باید پاسخ را به صورت چرخه‌ای بخواند: اندازه chunk را بخواند، سپس داده‌های با اندازه مشخص شده را بخواند، سپس تکرار کند. فرآیند زمانی پایان می‌یابد که یک chunk با اندازه صفر دریافت شود. Chunked Transfer بخش اجباری HTTP/1.1 است و توسط تمام سرورهای وب مدرن و کلاینت‌های HTTP پشتیبانی می‌شود.

دلیل اصلی استفاده از chunked transfer تولید پویای محتوا است. وقتی سرور پاسخ را بر اساس درخواست پایگاه داده، API خارجی یا محاسبات طولانی تولید می‌کند، نمی‌تواند از قبل اندازه نتیجه را بداند. به جای بافر کردن کل پاسخ در حافظه (که برای حجم‌های بزرگ خطرناک است)، سرور Transfer-Encoding: chunked را فعال می‌کند و داده‌ها را با آماده شدنشان ارسال می‌کند. این به ویژه برای سرورهای با حافظه محدود و پاسخ‌هایی که اندازه آنها می‌تواند بسیار بزرگ باشد — از 100 مگابایت و بیشتر — مهم است.

تفاوت بین HTTP/1.1 chunked و HTTP/2

در HTTP/2 مکانیزم chunked transfer به عنوان یک قابلیت مجزا وجود ندارد، زیرا پروتکل از مالتی‌پلکس کردن جریان‌ها در سطح فریم استفاده می‌کند. در HTTP/2 داده‌های با هر اندازه‌ای در فریم‌های DATA منتقل می‌شوند و اندازه بدنه پاسخ نیازی به اعلام قبلی ندارد — جریان می‌تواند در هر لحظه بسته شود. سرورهای مدرن به طور خودکار پاسخ‌های chunked HTTP/1.1 را هنگام پراکسی به upstream HTTP/2 به انتقال جریانی معادل تبدیل می‌کنند. Chunked Transfer برای اتصالات HTTP/1.1 همچنان کاربردی است.

انتقال تکه‌ای چگونه کار می‌کند

وقتی سرور تصمیم به استفاده از Chunked Transfer می‌گیرد، Content-Length را محاسبه نمی‌کند و هدر Transfer-Encoding: chunked را ارسال می‌کند. سپس بدنه پاسخ به صورت دنباله‌ای از chunkها تشکیل می‌شود. هر chunk با خطی حاوی اندازه chunk در فرمت هگزادسیمال (بدون پیشوند 0x) شروع می‌شود و پس از آن CRLF ( ) می‌آید. سپس داده‌های chunk با اندازه مشخص شده و CRLF می‌آید. آخرین chunk اندازه 0 دارد و پس از آن ممکن است هدرهای trailer وجود داشته باشند.

اندازه هگزادسیمال امکان انتقال chunkهای با هر اندازه‌ای را فراهم می‌کند — از 1 بایت تا حجم تئوری نامحدود. در عمل اندازه chunk توسط سرور انتخاب می‌شود: مقادیر معمول 4 کیلوبایت، 8 کیلوبایت یا 16 کیلوبایت هستند. اندازه بهینه chunk باید مضربی از اندازه بخش TCP (معمولاً 1460 بایت برای Ethernet) باشد تا تکه‌تکه شدن در سطح حمل و نقل به حداقل برسد. Nginx به طور پیش‌فرض از chunkهای 4 کیلوبایتی استفاده می‌کند، Apache — 8 کیلوبایتی.

کلاینتی که Transfer-Encoding: chunked دریافت کرده است، موظف است پاسخ را chunk به chunk تا chunk صفر پایانی بخواند. اگر کلاینت از chunked transfer پشتیبانی نکند، سرور نمی‌تواند از این حالت استفاده کند. در عمل تمام کلاینت‌های HTTP مدرن — مرورگرها، OkHttp، URLSession، curl — به طور کامل از پاسخ‌های chunked پشتیبانی می‌کنند. خواندن جریانی به کلاینت امکان می‌دهد پردازش داده‌ها را قبل از دریافت پاسخ کامل آغاز کند که برای عملکرد حیاتی است.

عنصر chunkفرمتمثال
اندازه chunkHEX + CRLF1000
داده‌های chunk[تعداد بایت] + CRLF[4096 بایت داده]
chunk پایانی0 0
Trailer (اختیاری)هدرها + CRLFExpires: Wed, 21 Oct 2025

هدرهای trailer در Chunked Transfer

Chunked Transfer از هدرهای trailer پشتیبانی می‌کند — هدرهای HTTP اضافی که بعد از آخرین chunk ارسال می‌شوند. این برای فراداده‌هایی مفید است که تنها پس از اتمام تولید پاسخ مشخص می‌شوند: مثلاً Content-MD5 یا X-Compression-Ratio. هدرهای trailer باید در هدر Trailer اعلام شوند: Content-MD5, X-Compression-Ratio. در عمل trailer به ندرت استفاده می‌شوند — بیشتر سرورها آنها را در پاسخ‌های خود قرار نمی‌دهند.

فرمت پاسخ chunked

پاسخ chunked ساختار کاملاً مشخصی دارد که کلاینت باید آن را به درستی تجزیه کند. بیایید یک مثال کامل از پاسخ HTTP با Transfer-Encoding: chunked را بررسی کنیم. بعد از هدرها و خط خالی، بدنه پاسخ شروع می‌شود. ساختار بدنه دنباله‌ای است: اندازه_chunk داده‌ها اندازه_chunk داده‌ها ... تا 0 . هر اندازه با کاراکترهای ASCII در سیستم اعداد هگزادسیمال ارسال می‌شود.

مثال پاسخ سرور با Chunked Transfer:
HTTP/1.1 200 OK
Content-Type: text/plain
Transfer-Encoding: chunked

7
Hello
6
World!
0

در این مثال سرور رشته «Hello World!» را با دو chunk ارسال می‌کند. اولین chunk با اندازه 7 بایت حاوی «Hello » است، دومین — 6 بایت «World!». کلاینت داده‌ها را از هر دو chunk جمع‌آوری می‌کند و رشته کامل را دریافت می‌کند. مهم: اندازه chunk فقط داده‌ها را شامل می‌شود، بدون در نظر گرفتن جداکننده‌های CRLF خود chunkها. chunk خالی پایانی (0 ) به کلاینت از پایان انتقال خبر می‌دهد.

kotlin
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()
}

تجزیه پاسخ chunked در OkHttp

OkHttp برنامه‌نویس را کاملاً از جزئیات Chunked Transfer دور می‌کند. هنگام دریافت پاسخ با Transfer-Encoding: chunked، OkHttp به طور خودکار chunkها را جمع‌آوری می‌کند و بدنه کامل پاسخ را از طریق response.body?.string() در اختیار برنامه‌نویس قرار می‌دهد. برای پردازش جریانی از response.body?.source() استفاده می‌شود که BufferedSource را برمی‌گرداند و امکان خواندن داده‌ها را با دریافتشان فراهم می‌کند. برنامه‌نویس نیازی به تجزیه دستی اندازه‌های hex و CRLF ندارد — کتابخانه این کار را به طور خودکار انجام می‌دهد.

Chunked Transfer در مقابل Content-Length

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 برای مواردی تعیین شده است که سرور پراکسی بدنه پاسخ را تغییر می‌دهد و نمی‌تواند Content-Length اصلی را حفظ کند. برخی کلاینت‌های HTTP قدیمی این وضعیت را به درستی پردازش نمی‌کنند، اما پیاده‌سازی‌های مدرن از مشخصات پیروی می‌کنند.

زمانی که Content-Length غیرممکن است

سناریوهایی وجود دارد که در آنها Content-Length اصولاً نمی‌تواند از قبل محاسبه شود. گزارش‌های پویا که بر اساس درخواست کاربر با فیلتر و تجمیع تولید می‌شوند — سرور حجم داده را تا پایان درخواست پایگاه داده نمی‌داند. ویدئوی جریانی که در زمان واقعی از دوربین ارسال می‌شود — اندازه نامحدود است. SSE و long polling برای اعلان‌ها — پاسخ ممکن است به طور نامحدود ادامه یابد. در همه این موارد Chunked Transfer تنها مکانیزم صحیح است.

Streaming مبتنی بر chunked transfer

Chunked Transfer اساس بسیاری از فناوری‌های جریانی در وب است. معروف‌ترین آنها Server-Sent Events (SSE) است که در آن سرور رویدادها را از طریق یک اتصال HTTP با Transfer-Encoding: chunked به کلاینت ارسال می‌کند. SSE از فرمت متنی خاصی (data: پیام ) استفاده می‌کند، اما لایه انتقال chunked transfer معمولی است. مرورگر رویدادها را با ارسال سرور دریافت می‌کند، بدون اینکه منتظر پایان پاسخ باشد.

جریان صدا و ویدئو نیز بر Chunked Transfer متکی است. سرورهای رسانه‌ای مانند Nginx RTMP و Wowza Streaming Engine داده‌های رسانه‌ای را از طریق HTTP به صورت chunk ارسال می‌کنند. پخش‌کننده سمت کلاینت پس از دریافت اولین chunk شروع به پخش می‌کند، بدون اینکه منتظر بارگذاری کامل فایل باشد. این زمان شروع پخش (Time to First Frame) را از ده‌ها ثانیه به 1-2 ثانیه کاهش می‌دهد. YouTube و Netflix دقیقاً از این رویکرد برای جریان‌های HTTP خود استفاده می‌کنند.

در توسعه اپلیکیشن‌های موبایل، Chunked Transfer برای انتقال حجم‌های زیاد داده بدون بارگذاری کل پاسخ در حافظه استفاده می‌شود. هنگام بارگذاری تصاویر از طریق Coil یا Glide در اندروید، کتابخانه‌ها داده‌های جریانی را به صورت chunk می‌خوانند و تصویر را تدریجاً رمزگشایی می‌کنند. این امکان نمایش تصاویر بزرگ (10+ مگابایت) را بدون OutOfMemoryError فراهم می‌کند. OkHttp از خواندن جریانی از طریق response.body?.byteStream() پشتیبانی می‌کند که InputStream را برمی‌گرداند و داده‌ها را chunk به chunk می‌خواند.

Chunked transfer در gRPC و GraphQL

gRPC از HTTP/2 استفاده می‌کند که در آن انتقال جریانی در سطح پروتکل تعبیه شده است و نیازی به مکانیزم جداگانه chunked ندارد. سرورهای GraphQL که از طریق HTTP/1.1 کار می‌کنند می‌توانند از Chunked Transfer برای انتقال جریانی نتایج اشتراک‌ها (subscriptions) استفاده کنند. Apollo Server و Hasura پاسخ‌های chunked را برای اشتراک‌های GraphQL ارسال می‌کنند و رویدادها را با وقوعشان منتقل می‌کنند. کلاینت به‌روزرسانی‌های بلادرنگ را بدون نیاز به polling دریافت می‌کند.

مزایا و محدودیت‌ها

Chunked Transfer مزایای مهمی برای برنامه‌های وب فراهم می‌کند. ارسال فوری داده‌ها — سرور پاسخ را قبل از ارسال بافر نمی‌کند و تأخیر تا اولین بایت را کاهش می‌دهد. پردازش جریانی — کلاینت می‌تواند پردازش داده‌ها را با دریافتشان آغاز کند، بدون اینکه منتظر بارگذاری کامل باشد. عدم محدودیت حافظه — سرور پاسخ کامل را در حافظه ذخیره نمی‌کند که برای حجم‌های زیاد داده حیاتی است. امکان انتقال جریان‌های بی‌نهایت — SSE، ویدئوی زنده، مانیتورینگ.

با این حال Chunked Transfer محدودیت‌هایی دارد. سربار برای هر chunk 6-12 بایت برای اندازه + CRLF است که برای تعداد زیادی chunk کوچک (مثلاً 100 بایت) می‌تواند اندازه پاسخ را 10-15% افزایش دهد. عدم امکان مشخص کردن اندازه دقیق — کلاینت نمی‌تواند از قبل بافر اختصاص دهد یا نوار پیشرفت نشان دهد. مشکلات با سرورهای پراکسی — برخی پراکسی‌های قدیمی از chunked transfer پشتیبانی نمی‌کنند و نمی‌توانند چنین پاسخ‌هایی را کش کنند. عدم پشتیبانی از ادامه بارگذاری — برای پاسخ‌های chunked که بخشی از آنها دریافت شده نمی‌توان درخواست Range انجام داد.

طبق HTTP Archive, 2025، حدود 35% از تمام پاسخ‌های HTTP از Transfer-Encoding: chunked استفاده می‌کنند. در میان آنها صفحات پویا (60%)، پاسخ‌های API (25%) و جریان‌های رسانه‌ای (15%) غالب هستند. فایل‌های ثابت تقریباً همیشه از Content-Length استفاده می‌کنند زیرا اندازه آنها از قبل مشخص است. سهم پاسخ‌های chunked به تدریج با گسترش HTTP/2 کاهش می‌یابد، زیرا در HTTP/2 انتقال جریانی در سطح فریم بدون نیاز به هدر اضافی Transfer-Encoding پیاده‌سازی شده است.

توصیه‌های عملی

در توسعه اپلیکیشن‌های موبایل از Chunked Transfer برای بارگذاری فایل‌های بزرگ (تصاویر، ویدئو) و برای درخواست‌های API که آرایه‌های بزرگ داده برمی‌گردانند استفاده کنید. OkHttp به طور کامل از chunked transfer بدون پیکربندی اضافی پشتیبانی می‌کند. برای آپلود به سرور (upload) Chunked Transfer استفاده نمی‌شود — برای این کار از Transfer-Encoding برای آپلود در HTTP/1.1 استفاده نمی‌شود. در iOS، URLSession بدون پیکربندی ویژه از هر دو ارسال و دریافت داده‌های chunked پشتیبانی می‌کند. تجزیه جریانی JSON (مثلاً از طریق Jackson Streaming API یا Moshi) امکان پردازش آرایه‌های بزرگ JSON را با دریافت جریان chunked فراهم می‌کند.

سوالات متداول

سرور چگونه Chunked Transfer را فعال می‌کند؟

سرور Chunked Transfer را به طور خودکار فعال می‌کند زمانی که اندازه پاسخ مشخص نیست. Nginx اگر Content-Length تنظیم نشده باشد، Transfer-Encoding: chunked را اضافه می‌کند. در Spring Boot، StreamingResponseBody و SseEmitter به طور خودکار از chunked transfer استفاده می‌کنند. در Node.js Express اگر res.write() و res.end() بدون Content-Length فراخوانی شوند، پاسخ chunked می‌شود.

آیا می‌توان از Content-Length و chunked همزمان استفاده کرد؟

خیر، مشخصات HTTP/1.1 استفاده همزمان از Content-Length و Transfer-Encoding: chunked را ممنوع می‌کند. اگر سرور هر دو هدر را ارسال کند، کلاینت باید Content-Length را نادیده بگیرد و پاسخ را به عنوان chunked پردازش کند. این قانون در RFC 7230 برای سازگاری با سرورهای پراکسی که ممکن است بدنه پاسخ را تغییر دهند تعیین شده است.

چه اندازه chunk بهینه است؟

اندازه بهینه chunk به سناریو بستگی دارد. برای صفحات وب معمولی — 4-8 کیلوبایت. برای انتقال جریانی ویدئو — 16-64 کیلوبایت. برای SSE — حداقل chunkهای 1-2 کیلوبایت برای کاهش تأخیر. اندازه chunk باید مضربی از اندازه بخش TCP (1460 بایت برای Ethernet) باشد تا تکه‌تکه شدن در سطح حمل و نقل به حداقل برسد.

آیا Chunked Transfer از طریق پراکسی کار می‌کند؟

سرورهای پراکسی مدرن (Nginx، HAProxy، Envoy) از Chunked Transfer پشتیبانی می‌کنند. پراکسی می‌تواند chunkها را بدون بافر کردن عبور دهد (streaming) یا کل پاسخ را بافر کرده و با Content-Length دوباره ارسال کند. پراکسی‌های قدیمی ممکن است پاسخ chunked را تا پایان بافر کنند که تأخیر را افزایش می‌دهد. HTTP/2 این مشکل را در سطح پروتکل حل می‌کند.

تفاوت Chunked Transfer با HTTP chunked encoding چیست؟

اینها یکسان هستند. Chunked Transfer نام کامل مکانیزم از مشخصات HTTP/1.1 است. HTTP chunked encoding همان چیز است که گاهی در مستندات کتابخانه‌ها استفاده می‌شود. Transfer-Encoding: chunked هدری است که این حالت را فعال می‌کند. هر سه اصطلاح مکانیزم یکسانی از انتقال داده‌ها به صورت تکه‌ای را توصیف می‌کنند.

خلاصه

  • Chunked Transfer — مکانیزم HTTP/1.1 که بدنه پاسخ را بدون مشخص کردن قبلی Content-Length به صورت chunk انتقال می‌دهد.
  • Transfer-Encoding: chunked — هدری که انتقال chunked را فعال می‌کند؛ هر chunk شامل اندازه hex، داده‌ها و CRLF است.
  • chunk نهایی با اندازه صفر پایان انتقال را نشان می‌دهد و پس از آن می‌توانند هدرهای trailer قرار گیرند.
  • Dynamic content streaming — کاربرد اصلی: صفحات پویا، SSE، صدا/ویدئوی جریانی، گزارش‌های طولانی.
  • Content-Length و chunked منحصر به فرد هستند — مشخصات استفاده همزمان از این هدرها را ممنوع می‌کند.
  • مزایا — کاهش تأخیر، صرفه‌جویی در حافظه، امکان انتقال جریان‌های بی‌نهایت و پردازش جریانی در سمت کلاینت.
  • محدودیت‌ها — سربار هدرهای chunk، عدم امکان نمایش پیشرفت، مشکلات با سرورهای پراکسی قدیمی.

ما یک اپلیکیشن موبایل به صورت کلید در دست توسعه خواهیم داد

IT Sectr از سال 2017 برنامه‌های iOS و Android را برای استارتاپ‌ها و کسب‌وکارها ایجاد می‌کند. ما به شما مشاوره می‌دهیم و بهترین راه‌حل را پیشنهاد خواهیم کرد.

بحث درباره پروژه

همچنین بخوانید