php 64 lines · 10 steps

How HTTP range requests stream files in PHP

A PHP file streamer that honors the Range header to serve partial content for resumable and seekable downloads.

Explained by highlit
1<?php
2 
3function streamFileWithRanges(string $path, ?string $rangeHeader): void
4{
5 if (!is_file($path)) {
6 http_response_code(404);
7 return;
8 }
9 
10 $size = filesize($path);
11 $mime = mime_content_type($path) ?: 'application/octet-stream';
12 $handle = fopen($path, 'rb');
13 
14 header('Content-Type: ' . $mime);
15 header('Accept-Ranges: bytes');
16 header('Content-Disposition: attachment; filename="' . basename($path) . '"');
17 
18 $start = 0;
19 $end = $size - 1;
20 
21 if ($rangeHeader !== null && preg_match('/^bytes=(\d*)-(\d*)$/', trim($rangeHeader), $m)) {
22 if ($m[1] === '' && $m[2] === '') {
23 http_response_code(416);
24 header('Content-Range: bytes */' . $size);
25 fclose($handle);
26 return;
27 }
28 
29 if ($m[1] === '') {
30 $start = max(0, $size - (int) $m[2]);
31 } else {
32 $start = (int) $m[1];
33 if ($m[2] !== '') {
34 $end = min((int) $m[2], $size - 1);
35 }
36 }
37 
38 if ($start > $end || $start >= $size) {
39 http_response_code(416);
40 header('Content-Range: bytes */' . $size);
41 fclose($handle);
42 return;
43 }
44 
45 http_response_code(206);
46 header("Content-Range: bytes {$start}-{$end}/{$size}");
47 } else {
48 http_response_code(200);
49 }
50 
51 $length = $end - $start + 1;
52 header('Content-Length: ' . $length);
53 
54 fseek($handle, $start);
55 $chunkSize = 8192;
56 while ($length > 0 && !feof($handle)) {
57 $read = min($chunkSize, $length);
58 echo fread($handle, $read);
59 flush();
60 $length -= $read;
61 }
62 
63 fclose($handle);
64}
01 / 01
STEP 01

Walkthrough

Space play ←→ step click any line
Three takeaways
  1. 1The Range header lets clients request byte slices, enabling resumable downloads and media seeking.
  2. 2Invalid or unsatisfiable ranges must return 416 with a Content-Range that reports the full size.
  3. 3Streaming in fixed chunks with flush() keeps memory flat regardless of file size.

Related explainers

Share this explainer

Here's the card — post it anywhere.

How HTTP range requests stream files in PHP — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code