Если ваш проект на PHP и перед отправкой файла (скачиванием пользователем файла) требуется проверить доступ к этому файлу у пользователя, а также не давать возможность скачивать «разные части» файла без доступа к этому файлу у пользователя, то реализовать это можно с помощью PHP скрипта/кода.
В самом простом случае можно отдать файл так
return file_get_contents($pathToBigVideoFile);
Где $pathToBigVideoFile — это путь до файл на сервере. Предположим, что ограничение на memory_limit = 128МБ, а размер файла = 256МБ. Тогда код выше будет выдавать ошибку
Fatal error: Allowed memory size
Чтобы обойти это ограничение, будем отдавать файл по частям, но нужно разобраться как сказать «браузеру» о таком способе передачи файла. И для этого воспользуемся знаниями о протоколе HTTP.
В RFC есть код ответа = 206 → https://httpwg.org/specs/rfc9110.html#status.206 , который позволяет сообщить клиенту (в нашем случае это будет браузер) о том что отдаваемый контент является «частичным». А чтобы перейти в «частичное представление» (т.е чтобы перевести браузер в режим частичного чтения контента), необходимо отдать специаильный заголовок Accept-Ranges → https://httpwg.org/specs/rfc9110.html#field.accept-ranges
Отдавая заголвок Accept-Ranges: bytes, мы сообщим клиенту инфомрацию о том что последующие ответы будут содержать код ответа 206 и будут частичными, на что клиент (браузер) должен отреагировать и отправлять нам заголовок Range.
Далее надо понять что будет содержаться в Range и как мы должны обработать этот заголвок своим PHP-скриптом.
Подробно про Range можно прочитать тут https://httpwg.org/specs/rfc9110.html#range.requests
Нас интересует byte ranges → https://httpwg.org/specs/rfc9110.html#byte.ranges . Это будут диапозоны байт которые клиент запрашивает у сервера, и по этим диапозонам мы сможем читать части файла на сервере и отдавать их клиенту.
Когда PHP-скрипт получит запрос с заголовком Range, то на этот заголовок нужно ответить заголовком Content-Range https://httpwg.org/specs/rfc9110.html#field.content-range в котором нужно будет указать диапозон в байтах, в котором содержится «содержимое файла» и задать 206 код ответа. А также «Content-Range»-заголовок необходимо указывать с «Content-length»- заголовокм, в котором будет размер файла в байтах или «*» если размер файла неизвестен.
Ещё важным моменто будет указание заголовка «Content-type» это прописано тут https://httpwg.org/specs/rfc9110.html#status.206 т.к помимо указания диапозона байт в котором находится контент (Content-Range) треубется ещё и указание mime-типа этого контента. Поэтому важно указать верный mime-тип. Если это mp4-видео файл, то без Content-type: video/mp4 браузер не запустит «режим partial content». Теперь всё это можно реализовать в PHP-скрипте. Ниже пример кода.
<pre><small> ¨K12K отдаст ошибку Fatal error: Allowed memory size... * http://0.0.0.0:8181/?stream=1 -> воспроизведёт файл */ ini_set('display_errors', 1); ini_set('error_reporting', E_ALL); // Ваш путь до файла $pathToBigVideoFile = __DIR__ . '/video.mp4'; if(!file_exists($pathToBigVideoFile)) { die("Файл не найден {$pathToBigVideoFile}"); } $fileSize = filesize($pathToBigVideoFile); if($_GET['stream']) { // 1. Говорим браузеру принимать ответ по частям header('Accept-Ranges: bytes'); header('Content-Length', $fileSize); header('Content-type: video/mp4'); // 2. Проверяем если браузер запрашивает по частям, отадём по частям if(isset($_SERVER['HTTP_RANGE']) && $_SERVER['HTTP_RANGE']) { header("HTTP/1.1 206 Partial Content"); $start = 0; $end = $fileSize - 1; $range = $_SERVER['HTTP_RANGE']; if (str_starts_with($range, 'bytes=')) { $rangeExp = array_filter(explode('bytes=', $range)); [$start, $end] = explode('-', implode('', $rangeExp)); $start = (int) $start; $end = (int) $end; if($start > $end || empty($end)) { $end = $fileSize - 1; } } // Вычисляем на основе входных данных от браузера размера данных который нужно отдать клиенту $chunkSizeBytes = $start - $end <= 0 ? 1024 : $start - $end; header("Content-range: bytes {$start}-{$end}/{$fileSize}"); // 3. По частям читаем файл и отдаём даныне в буфер, по достижению размеров Content-range, браузер // будет запрашивать контент по новой с другим Range $fd = fopen($pathToBigVideoFile, 'r'); // move to begining fseek($fd, $start); ob_flush(); while(!feof($fd)) { $content = fread($fd, $chunkSizeBytes); echo $content; flush(); } fclose($fd); } die; } elseif($_GET['file_get_contents']) { return file_get_contents($pathToBigVideoFile); } echo "Размер memory_limit: ".ini_get('memory_limit'). "\n"; echo "Размер файла: " . filesize($pathToBigVideoFile). "\n"; die(); </small> </pre>
Чтобы проверить работу скрипта. В нёмдостаточно подставить корректный путь до файла в переменную $pathToBigVideoFile и запустить встроенный в PHP, а дальше пройти по ссылка в комментариях скрипта. Таким образом PHP работающий с минимальным использованием памяти способен отдавать большие файлы в браузер или любой HTTP-клиент, который поддерживает работу с partial content.





