Pull to refresh

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

Reading time7 min
Views6.1K
В этом топике вы узнаете как:
  • восстановить повреждённую закачку, даже если файла нет в торрентах и других источниках, что содержат хеш его фрагментов;
  • скачать файл с докачкой, даже если она не поддерживается сервером;
  • докачивать, если сервер не даёт прямых ссылок, отдавая файл с разных адресов (но отдаёт 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) * $chunk1;

    
// получаем имя и хеш кусочка из файла с хешами
    
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:
Hubs:
+8
Comments4

Articles

Change theme settings