Первое, что вам нужно сделать, это отправить Accept-Ranges: bytes
заголовок во всех ответах, чтобы сообщить клиенту, что вы поддерживаете частичное содержимое. Затем, если запрос с Range: bytes=x-y
заголовком принимается (с x
и y
являющихся числами) вы разбираете диапазон клиент запрашивает, откройте файл , как обычно, стремятся x
байт вперед и отправить следующие y
- x
байты. Также установите ответ на HTTP/1.0 206 Partial Content
.
Без каких-либо проверок это могло бы работать более или менее:
$filesize = filesize($file);
$offset = 0;
$length = $filesize;
if ( isset($_SERVER['HTTP_RANGE']) ) {
// if the HTTP_RANGE header is set we're dealing with partial content
$partialContent = true;
// find the requested range
// this might be too simplistic, apparently the client can request
// multiple ranges, which can become pretty complex, so ignore it for now
preg_match('/bytes=(\d+)-(\d+)?/', $_SERVER['HTTP_RANGE'], $matches);
$offset = intval($matches[1]);
$length = intval($matches[2]) - $offset;
} else {
$partialContent = false;
}
$file = fopen($file, 'r');
// seek to the requested offset, this is 0 if it's not a partial content request
fseek($file, $offset);
$data = fread($file, $length);
fclose($file);
if ( $partialContent ) {
// output the right headers for partial content
header('HTTP/1.1 206 Partial Content');
header('Content-Range: bytes ' . $offset . '-' . ($offset + $length) . '/' . $filesize);
}
// output the regular HTTP headers
header('Content-Type: ' . $ctype);
header('Content-Length: ' . $filesize);
header('Content-Disposition: attachment; filename="' . $fileName . '"');
header('Accept-Ranges: bytes');
// don't forget to send the data too
print($data);
Возможно, я упустил что-то очевидное, и я определенно проигнорировал некоторые потенциальные источники ошибок, но это должно быть началом.
Здесь есть описание частичного содержимого, и я нашел некоторую информацию о частичном содержимом на странице документации для fread .
$length
будет отрицательным.$length = (($matches[2]) ? intval($matches[2]) : $filesize) - $offset;
исправляет это. 2.Content-Range
обрабатывает первый байт как байт0
, следовательно, последний байт$filesize - 1
. Следовательно, так и должно быть($offset + $length - 1)
.EDIT 2017/01 - я написал библиотеку , чтобы сделать это в PHP> = 7,0 https://github.com/DaveRandom/Resume
РЕДАКТИРОВАТЬ 2016/02 - Код полностью переписан в набор модульных инструментов в качестве примера использования, а не монолитной функции. Исправления, упомянутые в комментариях ниже, были внесены.
Проверенное рабочее решение (в значительной степени основанное на ответе Тео выше), которое имеет дело с возобновляемыми загрузками в наборе из нескольких автономных инструментов. Для этого кода требуется PHP 5.4 или новее.
Это решение по-прежнему может обрабатывать только один диапазон на запрос, но при любых обстоятельствах со стандартным браузером, о котором я могу думать, это не должно вызывать проблем.
Пример использования:
источник
$start = $end - intval($range[0]);
должно бытьrange[1]
Да. Поддержите изменения. См. RFC 2616, раздел 14.35 .
Это в основном означает, что вы должны прочитать
Range
заголовок и начать обслуживание файла с указанного смещения.Это означает, что вы не можете использовать readfile (), поскольку он обслуживает весь файл. Вместо этого сначала используйте fopen () , затем fseek () в правильную позицию, а затем используйте fpassthru () для обслуживания файла.
источник
echo file_get_contents(...)
не заработало (ООМ). Так что я не думаю, что это проблема. PHP 5.3.fpassthru
не удастся даже для файлов размером 50 МБ. Вам определенно не следует использовать его, если вы обслуживаете большие файлы на слабой конфигурации сервера. Как правильно указывает @Wimmer,fread
+print
- это все, что вам нужно в этом случае.Это работает на 100%, супер проверьте, я им пользуюсь, и проблем больше нет.
источник
Действительно хороший способ решить эту проблему без необходимости «катить свой собственный» PHP-код - это использовать модуль Apache mod_xsendfile. Затем в PHP вы просто устанавливаете соответствующие заголовки. Apache делает свое дело.
источник
XSendFilePath <absolute path> [AllowFileDelete]
( Tn123.org/mod_xsendfile/beta ).Если вы хотите установить новый модуль PECL, самый простой способ поддержать возобновляемые загрузки с помощью PHP - это выполнить
http_send_file()
, напримеристочник: http://www.php.net/manual/en/function.http-send-file.php
Мы используем его для обслуживания контента, хранящегося в базе данных, и он работает как шарм!
источник
В верхнем ответе есть различные ошибки.
bytes a-b
должно означать[a, b]
вместо[a, b)
, иbytes a-
не обрабатывается.Вот мой измененный код:
источник
ini_set('memory_limit', '-1');
?if(!isset($matches[2])) { $end=$fs-1; } else { $end = intval($matches[2]); }
Да, вы можете использовать для этого заголовок Range. Для полной загрузки клиенту нужно передать еще 3 заголовка:
Затем для прерванной загрузки вам необходимо проверить заголовок запроса Range:
И в этом случае не забудьте передать контент с кодом статуса 206:
Вы получите переменные $ start и $ to из заголовка запроса и воспользуетесь функцией fseek () для поиска правильной позиции в файле.
источник
У меня это сработало очень хорошо: https://github.com/pomle/php-serveFilePartial
источник
Небольшой класс с поддержкой композитора, который работает так же, как pecl http_send_file. Это означает поддержку возобновляемых загрузок и ограничения. https://github.com/diversen/http-send-file
источник
Возобновление загрузки по протоколу HTTP осуществляется через
Range
заголовок. Если запрос содержитRange
заголовок, и если другие индикаторы (напримерIf-Match
,If-Unmodified-Since
) указывают на то, что содержимое не изменилось с момента начала загрузки, вы даете код ответа 206 (а не 200), указываете диапазон возвращаемых байтов. вContent-Range
заголовке, затем укажите этот диапазон в теле ответа.Однако я не знаю, как это сделать в PHP.
источник
Спасибо, Тео! ваш метод не работал напрямую для потоковой передачи divx, потому что я обнаружил, что проигрыватель divx отправлял диапазоны, такие как bytes = 9932800-
но он показал мне, как это сделать, так что спасибо: D
источник
Вы можете использовать приведенный ниже код для поддержки запроса диапазона байтов в любом браузере.
источник