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