| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100 |
- import os
- import time
- import requests
- from tools.log import logger
- from tools.utils import Utils
- class FileDownloader:
- def __init__(self, url, local_filename, max_retries=1):
- self.url = url
- self.local_filename = local_filename
- self.max_retries = max_retries
- self.chunk_size = 8192 # 每块数据大小 (8 KB)
- self.timer = Utils.Timer()
- self.last_logged_time = 0
- self.last_logged_size = 0
- self.total_size = 0
- def download(self, resume=True):
- retry_count = 0
- retry_delay = 2
- while retry_count <= self.max_retries:
- try:
- return self._download(resume)
- except requests.RequestException as e:
- retry_count += 1
- logger.error(f"Download failed: {e}. Retrying {retry_count}/{self.max_retries} after {retry_delay}s...")
- time.sleep(retry_delay)
- retry_delay *= 2 # 指数退避
- raise Exception(f"Failed to download {self.url} after {self.max_retries} retries")
- def _download(self, resume):
- resume_header = {}
- downloaded_size = 0
- # 检查是否需要断点续传
- if resume and os.path.exists(self.local_filename):
- downloaded_size = os.path.getsize(self.local_filename)
- resume_header = {'Range': f'bytes={downloaded_size}-'}
- logger.info(f"Resuming download from byte: {downloaded_size}")
- else:
- logger.info("Starting new download.")
- # 发起请求
- with requests.get(self.url, headers=resume_header, stream=True, timeout=30) as response:
- response.raise_for_status()
- self.total_size = int(response.headers.get('Content-Length', 0)) + downloaded_size
- with open(self.local_filename, 'ab') as f:
- start_time = time.time()
- previous_time = start_time
- for chunk in response.iter_content(chunk_size=self.chunk_size):
- if not chunk: # 忽略空数据块
- continue
- f.write(chunk)
- downloaded_size += len(chunk)
- current_time = time.time()
- time_elapsed = int(current_time - previous_time)
- # 每隔 1 秒更新日志
- if time_elapsed >= 1 or self.timer.timer("download", 5):
- self._log_progress(downloaded_size, self.total_size, start_time, previous_time)
- previous_time = current_time
- logger.info(f"Download completed: {self.local_filename}")
- def _log_progress(self, downloaded_size, total_size, start_time, previous_time):
- """
- 输出下载进度日志,包含当前进度和下载速度。
- :param downloaded_size: 已下载大小(字节)
- :param total_size: 文件总大小(字节)
- :param start_time: 下载开始时间
- :param previous_time: 上一次记录日志的时间
- """
- percent = (downloaded_size / total_size) * 100
- elapsed_time = time.time() - start_time
- interval_time = time.time() - previous_time
- # 计算平均速度和瞬时速度
- average_speed = downloaded_size / elapsed_time if elapsed_time > 0 else 0
- instant_speed = (downloaded_size - self.last_logged_size) / interval_time if interval_time > 0 else 0
- # 转换为 MB/s
- average_speed_mb = average_speed / 1024 / 1024
- instant_speed_mb = instant_speed / 1024 / 1024
- logger.info(f"{os.path.basename(self.local_filename)} - Downloaded: "
- f"{downloaded_size / 1024 / 1024:.1f}MB of {total_size / 1024 / 1024:.1f}MB "
- f"({percent:.2f}%) | {instant_speed_mb:.2f} MB/s")
- # 更新记录的时间和大小
- self.last_logged_time = time.time()
- self.last_logged_size = downloaded_size
|