Произвольное чтение из удаленного (внешнего) файла
May 19, 09 by TracKerЗашел сегодня на AskDev и обнаружил такой вопрос.
Более чем уверен что каждый более-менее продвинутый (да и не только) пользователь интернета скачивал оттуда относительно большие файлы – например MP3. Особенно сложно это было сделать раньше когда был сплошной Dial-Up с отвратительным качеством связи и постоянными разрывами.
Единственный действенный способ скачать большие файлы был с помощью менеджера закачек, и было очень приятно когда сервер, на котором лежал желанный файл, поддерживал заветную докачку. Именно с ее помощью и можно прочитать любую область удаленного файла.
В Википедии написано:
The HTTP/1.1 webserver publishes its ability to respond to requests for certain byte ranges of the document by setting the header Accept-Ranges: bytes. This is useful if the client needs to have only certain portions of a resource sent by the server, which is called byte serving.
Подробнее читаем тут.
А теперь отвечаю на вопрос: доступ к произвольной части удаленного файла возможен, правда не всегда. Как я писал выше, необходимо чтобы сервер поддерживал функцию “докачки”.
Немного тестов и у меня получились вот такие две функции:
function isRestoreSupported($url) { $supported = false; $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_HEADER, 1); curl_setopt($ch, CURLOPT_NOBODY, 1); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); $content = curl_exec($ch); curl_close($ch); $content = trim(strtolower($content)); $headers = explode("\n", $content); foreach ($headers as $val) { $val = trim($val); if (substr($val, 0, 13) == "accept-ranges") { $pos = strpos($val, "bytes"); if ($pos === false) { $supported = false; } else { $supported = true; } } } unset($headers); return $supported; } function selectedRead($url, $position, $size) { $position_end = $position + $size; $hdr = array( "Range: bytes=$position-$position_end" ); $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_HEADER, 0); curl_setopt($ch, CURLOPT_NOBODY, 0); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_HTTPHEADER, $hdr); $content = curl_exec($ch); curl_close($ch); return $content; }
Не оптимизировано, но для демонстрации сгодится. Теперь находим какой-то файл и вызываем следующим образом:
$restore_support = isRestoreSupported("http://ru.akella.com/Files/Patches/N/NeverWinterNights2/nwn2rus104.exe"); if ($restore_support) { echo "Restore supported\n"; echo "Reading...\n"; echo "===[Data - Begin]===\n"; echo selectedRead("http://ru.akella.com/Files/Patches/N/NeverWinterNights2/nwn2rus104.exe", 0x4e, 39); echo "\n===[Data - End]===\n"; } else { echo "Restore not supported\n"; }
В результате имеем:
Restore supported Reading... ===[Data - Begin]=== This program cannot be run in DOS mode. ===[Data - End]===
Суть проста: сначала мы спрашиваем у сервера понимает ли он параметр заголовка Range (в народе – докачка), при этом с помощью CURLOPT_NOBODY ограничиваемся ответом сервера в виде одного заголовка, и в случае успеха скачиваем нужный нам кусок.
Шоубизнес: Хорошо спел Александр Рыбак потому и занял первое место на Евровидении, давно уже хочется сказки…
