Попытка загрузить серию архивов с ftp NCBI с использованием python ftplib, но ftplib зависает в конце длинной передачи файла

Я пытаюсь загрузить nr blastdabase с ftp-сайта NCBI (ftp: // ftp. ncbi.nlm.nih.gov/blast/db). Один из файлов довольно большой (16 ГБ), и его загрузка занимает некоторое время. По окончании загрузки этого файла программа просто зависает, не переходит к следующему файлу.

Часть моей программы, связанная с загрузкой файлов:

from pathlib import Path
import ftplib
from tqdm import tqdm

def _file_write_progress(block, fh, pbar):
    """Write ftp file and updates progress bar.

    Args:
        block (binary): Block of data received from ftp.retrbinary
        fh (BufferedWriter): Open file to write to in wb mode
        pbar (ProgressBar): Progress bar to update with download progress
    """
    fh.write(block)
    pbar.update(len(block))


def _download_ftp_files(url, remote_path, files_list, db_dir):
    """Download ftp file and update progress bar.

    Args:
        url (str): Url of ftp server to connect to
        remote_path (str): Path to directory containing tartget files
        files_list (list(str)): List of files to download
        db_dir (Path): Path to local directory to download files to
    """
    ftp = ftplib.FTP(url, timeout=3600)
    ftp.login()
    ftp.cwd(remote_path)
    for fn in tqdm(files_list, desc="Downloading file #"):
        with (db_dir / fn).open('wb') as fh:
            pbar = tqdm(desc=fn, total=ftp.size(fn))
            ftp.retrbinary(
                'RETR ' + fn,
                lambda block: _file_write_progress(block, fh, pbar),
                1024*1024
            )
    ftp.close()

Я думаю, что проблема связана с тайм-аутом ftp-соединения, но я не могу это исправить.

Я пробовал решения на Python: ftplib зависает в конце передачи и Python: загрузка файла использование ftplib зависает навсегда после успешной загрузки файла, но, похоже, это не работает.

Приведенный выше код изменен на основе этих ответов:

def _background(sock, fh, pbar):
    while True:
        block = sock.recv(1024*1024)
        if not block:
            break
        fh.write(block)
        pbar.update(len(block))


def _download_ftp_files(url, remote_path, files_list, db_dir):
    ftp = ftplib.FTP(url)
    ftp.login()
    ftp.cwd(remote_path)
    for fn in tqdm(files_list, desc="Downloading file #"):
        try:
            sock, size = ftp.ntransfercmd('RETR ' + fn)
            pbar = tqdm(desc=fn, total=size)
            with (db_dir / fn).open('wb') as fh:
                t = threading.Thread(target=_background(sock, fh, pbar))
                t.start()
                while t.is_alive():
                    t.join(60)
                    ftp.voidcmd('NOOP')
            sock.close()
        except ftplib.error_reply as e:
            print(e)

По какой-то причине это возвращает ftplib.error_reply 226 Transfer Completed в качестве исключения. Пытаюсь справиться, но программа просто зависает.

При необходимости я могу предоставить дополнительную информацию, любая помощь приветствуется!


person toasted_wheat    schedule 19.11.2020    source источник


Ответы (1)


Хорошо, я переключился на ftputil, который обертывает ftplib и, похоже, пока работает лучше.

Ниже приведен модифицированный код:

def _download_ftp_files(url, remote_path, files_list, db_dir):
"""Download ftp file and update progress bar.

Args:
    url (str): URL of ftp server to connect to
    remote_path (str): Path to directory containing tartget files
    files_list (list(str)): List of files to download
    db_dir (Path): Path to local directory to download files to
"""
with ftputil.FTPHost(url, user='anonymous', passwd='@anonymous') as ftp_host:
    ftp_host.chdir(remote_path)
    for fn in tqdm(files_list, desc="Downloading file #"):
        pbar = tqdm(desc=fn, total=ftp_host.path.getsize(fn))
        ftp_host.download(
            fn, str(db_dir / fn),
            lambda block: pbar.update(len(block)))
person toasted_wheat    schedule 19.11.2020