LINUX.ORG.RU

python докачка файла при обрыве коннекта

 , , ,


0

2

Привет, ЛОР!

Пишу скриптик на python, одна из функций которого - скачать файл. Файл большой, 2.5Гб. Вот кусок кода, который его качает:

local_file = urllib.URLopener()
local_file.retrieve(url, filename, reporthook=print_download_progress)
print(". Загрузка успешно окончена!\n")

Однако, иногда сервер разрывает соединение, и я получаю вот что:

Загружено: 39%, 1077960704 байтTraceback (most recent call last):
  File "/home/kir/PycharmProjects/Updater/main.py", line 156, in <module>
    if update_file(name):
  File "/home/kir/PycharmProjects/Updater/main.py", line 122, in update_file
    local_file.retrieve(url, filename, reporthook=print_download_progress)
  File "/usr/lib/python2.7/urllib.py", line 284, in retrieve
    "of %i bytes" % (read, size), result)
urllib.ContentTooShortError: retrieval incomplete: got only 1077955321 out of 2726195712 bytes

ЛОР, как организовать докачку файла?

local_file = urllib.URLopener()
try:
    local_file.retrieve(url, filename, reporthook=print_download_progress)
    print(". Загрузка успешно окончена!\n")
except urllib.ContentTooShortError:
    #тут код докачки файла с места обрыва
★★

Последнее исправление: kir64 (всего исправлений: 1)

Я не знаю, как это делалось в питоне 2, но в питоне 3.4 urllib.request.Request позволяет указывать заголовки запроса. В том числе Range.

proud_anon ★★★★★
()

Господа neversleep, proud_anon, true_admin! Большое спасибо за подсказку, переписал функцию вот на такую:

req = urllib2.Request(url)
u = urllib2.urlopen(req)
meta = u.info()
f = open(name, 'wb')
file_size = int(meta.getheaders("Content-Length")[0])

file_size_dl = 0
block_sz = 8192
while True:
    buffer = u.read(block_sz)
    if not buffer and file_size_dl < file_size:
        print("\n\nO-la-la! Гадкий сервер сбросил соединение. Возобновляю загрузку.\n\n")
        req.headers["Range"] = "bytes=%s-%s" %(file_size_dl, file_size)
        u = urllib2.urlopen(req)
    elif not buffer:
        break

    file_size_dl += len(buffer)
    f.write(buffer)

    sys.stdout.write("\rЗагружено: %3.2f%%, %d байт" % (file_size_dl * 100. / file_size, file_size_dl))
    sys.stdout.flush()

f.close()

На выходе получаю вот что:

Размер файла на сервере: 2726195712 байт
Загружено: 39.60%, 1079537672 байт

O-la-la! Гадкий сервер сбросил соединение. Возобновляю загрузку.


Загружено: 100.00%, 2726195712 байт
Размер файла на диске: 2726195712 байт

Проверяем корректность загрузки: 


Файл успешно обновлен!



Process finished with exit code 0

Еще раз, большое вам спасибо!

kir64 ★★
() автор топика
Ответ на: комментарий от kir64

А ты проверял, после скачивание md5 сходится? block_sz смело можешь делать раз в 10 больше. Вот мой многопоточный вариант на скорую руку который качает в много потоков и умеет докачку: https://github.com/kopchik/pdl

Я тоже не проверяю на эксепшены что гарантирует проблемы.

true_admin ★★★★★
()
Ответ на: комментарий от true_admin

md5 не проверяю, только сравниваю размер файла на диске и файла на сервере.

За ссылку на Porn Parallel DownLoader спасибо, изучу, что-то наверняка позаимствую.

Обработку исключений типа ответа сервера «404» или таймаута подключения буду прикручивать на стадии тестирования, сейчас основную логику бы написать. Это моё второе поделие на питоне, так что дается оно не очень легко.

kir64 ★★
() автор топика
Ответ на: комментарий от kir64

Проверь MD5 один раз при докачке. Чтобы убедиться что докачка работает как надо. Там арифметика простая, но off-by-one ошибки у меня были и файлы скачивались битыми. Не хочу чтобы у тебя тоже такие проблемы были.

true_admin ★★★★★
()
Ответ на: комментарий от true_admin

Спасибо за заботу, допишу проверку :)

Результат запощу сюда, в назидание потомкам (мало ли кто потом нагуглит эту тему).

kir64 ★★
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.