Произвольное чтение из удаленного (внешнего) файла

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 ограничиваемся ответом сервера в виде одного заголовка, и в случае успеха скачиваем нужный нам кусок.

Шоубизнес: Хорошо спел Александр Рыбак потому и занял первое место на Евровидении, давно уже хочется сказки…

google.com bobrdobr.ru del.icio.us technorati.com linkstore.ru news2.ru rumarkz.ru memori.ru moemesto.ru
Add your comment

One response for this post

  1. Хайпер Says:

    Несколько лет назад интернет у меня был намного дороже (о безлимитном интернете тогда даже в мечтах не думалось). Но была бесплатная почта :)
    И вот подобной штукой пользовался, чтобы кусками отправлять удалённые файлы себе на почту. Несколько гигов в месяц так на халяву сливал. Жутко рад был

Leave a Reply