Pobieranie plików około 10gb za pomocą http

Pobieranie plików około 10gb za pomocą http
Marcin Marcin
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 610
0

Potrzebuję pobierać pliki około 10gb każdy, następnie sprawdzać czy pobrały się poprawnie.
Za pomocą biblioteki requests nie działa to zawsze (w zależności od łącza działa albo nie działa)
Przykład z użyciem requests:

Kopiuj
                r = requests.get(url, stream=True)
                print(url)
                with open(save_product_name, 'wb') as fd:
                    for chunk in r.iter_content(chunk_size=512):
                        fd.write(chunk)

Szukając rozwiązania natknąłem się na post:
https://github.com/aio-libs/aiohttp/issues/2249#issuecomment-327896106

Zgodnie z zasadą copy-pasty-programmingu poczyniłem odpowiednie zmiany:

Kopiuj
import asyncio
import sys
from time import time

import aiohttp


async def async_download(file_url: str):
    total_size = 0
    start = time()
    print("start download " + file_url)
    file = open("test.zip", 'wb')
    async with aiohttp.ClientSession() as session:
        async with session.get(file_url, timeout=None) as r:
            while True:
                chunk = await r.content.read(16144)
                if not chunk:
                    break
                total_size += len(chunk)
                file.write(chunk)
                message = f'{time() - start:0.2f}s, downloaded: {total_size / (1024 * 1024):0.0f}MB'
                sys.stdout.write('\r' + message)
    file.close()
    sys.stdout.flush()

url = "http://speedtest.tele2.net/10GB.zip"
loop = asyncio.get_event_loop()
loop.run_until_complete(async_download(file_url=url))

Po uruchomieniu z innymi plikami (do których dostęp jest już za pomocą generowatnego tokena)
otrzymuję błąd:

Kopiuj
Traceback (most recent call last):
  File "C:\Users\Admin\Desktop\fsadas\test.py", line 28, in <module>
    loop.run_until_complete(async_download(file_url=url))
  File "C:\Users\Admin\AppData\Local\Programs\Python\Python38-32\lib\asyncio\base_events.py", line 616, in run_until_complete
    return future.result()
  File "C:\Users\Admin\Desktop\fsadas\test.py", line 16, in async_download
    chunk = await r.content.read(16144)
  File "C:\Users\Admin\AppData\Local\Programs\Python\Python38-32\lib\site-packages\aiohttp\streams.py", line 368, in read
    await self._wait('read')
  File "C:\Users\Admin\AppData\Local\Programs\Python\Python38-32\lib\site-packages\aiohttp\streams.py", line 296, in _wait
    await waiter
aiohttp.client_exceptions.ClientPayloadError: Response payload is not completed

Moje pytania:

  1. Jak pobierać tak duże pliki?
  2. W jaki sposób napisać tą funkcję tak, aby można było otworzyć około 20 sesji pobierania jednocześnie, każdy w innym wątku (może lepiej subprocess)?
  3. W jaki sposób sprawdzać czy plik pobrał się poprawnie?

Chciałbym napisać testy do tej funkcji - będę wdzięczny za podpowiedzi

AN
  • Rejestracja: dni
  • Ostatnio: dni
0

Jaki jest sens otwierania kilku sesji naraz i tym samym uruchamianie kilku transferów naraz? To nie przyspieszy pobierania, bo łącze po jednej lub drugiej stronie będzie wykorzystane do granic możliwości (po tej stronie, po której jest wolniejsze) przy pobieraniu jednego pliku. Lepiej już pobierać pliki pojedynczo, jeden za drugim.

Sprawdzanie pliku to po pierwsze uzyskanie informacji o wielkości pliku co do bajtu i po pobraniu sprawdzenie czy plik tyle ma. Natomiast poprawność treści sprawdzisz wykorzystując funkcje skrótu, np. MD5 lub SHA2. Na serwerze przelicz funkcję dla każdego pliku i udostępnij je. Po pobraniu pliku przelicz skrót po stronie klienta i porównaj go ze skrótem udostępnionym przez serwer. jeżeli skrót będzie taki sam, to z bardzo dużym prawdopodobieństwem plik jest pobrany poprawnie (jest identyczny po obu stronach).

Marcin Marcin
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 610
0

Dodałem taką procedurę:

Kopiuj
def d_download(url, path = "test1111111.zip"):
    r = requests.get(url, stream=True)
    chunk_size = 2 ** 20  # download in 1 MB chunks
    mode = "ab"
    with open(path, mode) as f:
        for chunk in r.iter_content(chunk_size=chunk_size):
            if chunk:  # filter out keep-alive new chunks
                f.write(chunk)

Działa
byłbym wdzięczny za podpowiedź jak napisać testy i zrobić asercje
Kodu nie przetestować w pełni więc będę wdzięczny za inne propozycje

lion137
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 5023
0

Ustaw sobie jakiś czas, po którym zrobic restart jeśli request sfailuje; zapuść to i niech ssie.

Zarejestruj się i dołącz do największej społeczności programistów w Polsce.

Otrzymaj wsparcie, dziel się wiedzą i rozwijaj swoje umiejętności z najlepszymi.