Pull to refresh

Лечение битых файлов, закачки и докачки

Lumber room
В этом топике вы узнаете как:
  • восстановить повреждённую закачку, даже если файла нет в торрентах и других источниках, что содержат хеш его фрагментов;
  • скачать файл с докачкой, даже если она не поддерживается сервером;
  • докачивать, если сервер не даёт прямых ссылок, отдавая файл с разных адресов (но отдаёт Partial Content).

Нынче наткнулся на топик «Торрент vs. 64 кбит/с» и увидел проблему с которой сам неделю назад столкнулся.

Довольно долго, дней 10 (с перерывами), качал 3,5 Гб ISO образ на своём 100 Кбит/с. И этот самый образ повредился при скачивании. Попытался поискать это дело в торрентах и воспользоваться методом, описанным в вышеупомянутом топике — раньше я уже так восстанавливал файлы. Но файла там не оказалось (да и на ресурс откуда я скачивал он попал всего-то пару недель назад). Дабы меня не начали пинать за пиратство, сразу скажу что качал Visual Studio 2008 Professional SP1 RUS с Dreamspark.

Так вот, ждать ещё 10 дней не хотелось, как и производить особо много ручной работы, в результате чего родился скрипт, который и выполнил весь этот грязный процесс…


Идея


Требовалось минимизировать объём информации которую придётся перекачать, и собственно как-то выявить эти повреждённые кусочки. Отсюда, очевидно вытекает план действий:
  • режем скачанный файл на достаточно мелкие кусочки;
  • считаем хеш;
  • сравнимаем с неким эталонным, выявляя «плохие»;
  • скачиваем нужные куски и склеиваем.

А где же взять этот самый «правильный» хеш? Чтобы не скачивать всё целиком себе, необходимо какое-то стороннее место, куда можно скачать файл с гораздо более высокой скоростью. У меня как раз имелся доступ к площадке виртуального хостинга, в том числе и SSH — как раз то что нам нужно. Но… пространства там было совсем не 3,5 Гб, а всего 1,5 Гб. В результате пришла мысль тянуть не весь файл сразу, а по кусочкам.

Код


В результате всех этих измышлений родился следующий код (назовём файл md5_verify.php).

<?php
$md5sum
= 'md5sum.txt'; // файл где хранятся хеши
$tmp = 'chunk.tmp'; // имя временного файла для очередного кусочка
$out = 'out/'; // сюда будут сложены кусочки которые надо будет перекачать

$offset = 0; // с какого места начинаем (в байтах)
$chunk = 1048576; // размер кусочка
$size = 3760066560; // размер файла

$host = 'http://all.files.dreamspark.com';

$path = '/dl/studentdownload/7/6/3/76329869-10C4-4360-9B09-98C813F8EAFA/ru_visual_studio_2008_professional_edition_dvd_x86_x15-25526.iso';
$param = '?LCID=1033&__gda__={timestamp}_{hash}';
$cookie = '__sdt__={another-hash-or-guid}';
$url = $host . $path . $param;

// читаем файл с хешами как массив строк
$sums = file($md5sum);

for (
$i = 0, $l = sizeof($sums); $i < $l && $offset + $i * $chunk < $size; $i++)
{
// начало и конец очередного кусочка
$start = $offset + $i * $chunk;
$end = min($size, $offset + ($i + 1) * $chunk) - 1;

// получаем имя и хеш кусочка из файла с хешами
list($hash, $file) = explode(' ', $sums[$i + intval($offset / $chunk)]);

$file = trim($file, "*\r\n ");

// создаём временный файл
$fp = fopen($tmp, "w+");

// устанавливаем параметры CURL
$options = array
(
CURLOPT_URL => $url,
CURLOPT_HEADER => false,
CURLOPT_COOKIE => $cookie,
CURLOPT_RANGE => $start . '-' . $end, // вот тут указывается какой диапазон байт качать
CURLOPT_FILE => $fp
);

$ch = curl_init(); // инициализируем CURL
curl_setopt_array($ch, $options); // устанавливаем параметры
curl_exec($ch); // скачивам
curl_close($ch); // убиваем объект
fclose($fp); // закрываем файл

// наш локальный кусочек плохой если его хеш не совпадает с тем что мы сейчас скачали
$broken = ($hash != md5_file($tmp));

// выводим имя файла, диапазон байт и статус этого кусочка
print $file.' ['.$start.'-'.$end.']: ' . ($broken ? 'BROKEN' : 'OK') . "\n";

// если файл был испорчен, то откладываем этот кусочек (чтобы потом его скачать)
if ($broken)
copy($tmp, $out.$file);

// удаляем временный файл
unlink($tmp);

}




Если кратко, скрипт по частям скачивает файл, сравнивая хеш каждого очередного кусочка, с тем что мы посчитали у себя на машине. Если не совпадает — наш кусочек битый, а тот что на сервере — хороший, откладываем его для последующего скачивания.

Действуем


Для начала, то что мы скачали надо разрезать на кусочки:
C:\ISO>mkdir out && cd out && split -a 3 -b 1048576 ..\ru_visual_studio_2008_professional_edition_dvd_x86_x15-25526.iso

Параметр -a указывает длину суффикса, так как он буквенный (26 символов нижнего регистра), то 3 нам будет вполне достаточно: 26³ = 17576, что явно больше количества кусочков 3,5 Гб / 1 Мб ≈ 3584.

Когда всё это дело порежется, получим файлы с именами xaaa, xaab, xaac и т. д.

Теперь считаем хеш:
C:\ISO\out>md5sum x* > ..\md5sum.txt

Этот процесс тоже довольно продолжительный, в итоге будет получен файл с содержимым вида:
26c379b3718d8a22466aeadd02d734ec *xaaa
2671dc8915abd026010f3d02a5655163 *xaab
6f539fcb0d5336dfd28df48bbe14dd20 *xaac
69f670a2d9f8cf843cdc023b746c3b8c *xaad



Затем загружаем md5_verify.php и md5sum.txt на сервер, и там же создаём папочку out, куда будут складываться кусочки которые нам надо будет перекачать. Подключаемся по SSH и тычем мордой php интерпретатор в наш скрипт. Теперь можно идти спать, гулять или смотреть House M.D.

Через несколько часов, в зависимости от ширины канала, которая вам предоставлена на площадке, работа скрипта завершится, а в папочке out будут лежать все кусочки, которые вам надо скачать.

Прячем их в архив:
tar - jcvf "chunks.tar.bz2" ./out/x*

Ну и наконец надо лишь переложить полученный архив в папочку откуда вы можете его скачать, и натравить на него свой любимый wget, curl или что вам там больше нравится.

Заменив поврежденные кусочки тем что извлекли из скачанного архива, можем склеить пациента обратно:
cat out\x* > fixed.iso

Можно резать, клеить и хешировать чем хочется, но все кому поневоле приходится пользоваться Windows давно раздобыли все эти маленькие замечательные утилитки типа cat, split и md5sum, стандартные для Linux, портированными для Windows (проект GnuWin32).

Про докачку


Да, кстати, вместе с этим нашёл отличный способ качать с докачкой даже если сервер не способен отдавать Partial Content: просто напросто тянем файл на свою хостинговую площадку через шелл, а потом уже качаем оттуда как приличные люди.

Многие сервисы конечно же хотят Referer и/или Cookie, не соглашаясь просто так отдать файл. Их можно получить: нужен Firefox и расширение к нему — Live HTTP Headers.

Открываем окошечко аддона, жмём на сайте на ссылку/кнопку «Скачать», выскакивает предложение сохранить файл. Отказываемся качать файл, нажав «Отмена», а затем копируем нужные нам данные из заголовка.

Live HTTP Headers

Ну а затем пихаем эти данные в свою любимую качалку (на скринах это всё производится локально, но вам, очевидно, надо будет это выполнить через SSH на удалённом сервере):
wget --header "Referer: http://csna01.libredigital.com/" -O "output.pdf" http://csna02.libredigital.com/cgi-bin/pdf_loader.pl?v=5

wget
curl -v -e "http://csna01.libredigital.com/" -o "output.pdf" http://csna02.libredigital.com/cgi-bin/pdf_loader.pl?v=5

curl

На скрине можно увидеть использование ключа -c для wget (для curl используется -C), он позволяет докачивать, впрочем приведён тут исключительно для демонстрации, ибо сервер из примера как раз не даёт докачать.

Напоследок

Впрочем, есть смысл от использования wget и curl с докачкой непосредственно на своей машине: если оставлять одним и тем же выходное имя файла (ключ -O), то можно качать один файл частями в любое время и использую каждый раз новую ссылку (и с изменившимся cookie).

Это и есть третье «узнаете как» из обещаний в начале топика.

P.S. Вся история завершилась тем что у пацента обнаружилось ≈ 500 повреждённых частей. Потрачено пара часов на написание, отладку, нарезку и подсчёт хеша, несколько часов на работу скрипта на сервере, и около 12 часов на скачивание этих частей, что в сумме заняло примерно сутки. Тем не менее это гораздо меньше чем опять качать частями в течении десятка дней (и обязательно что-нибудь снова повредить).
Tags:закачкахэшdownloadhashcurlдокачка
Hubs: Lumber room
Total votes 10: ↑9 and ↓1+8
Views2.6K

Popular right now