编辑代码

import requests
from concurrent.futures import ThreadPoolExecutor
import os
import logging
from tqdm import tqdm  # 用于显示进度条

# 配置日志格式
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')

def download_file(url, file_path, thread_count=4):
    try:
        # 获取远程文件信息
        response = requests.get(url, stream=True)
        response.raise_for_status()

        # 计算文件总大小
        file_size = int(response.headers.get('content-length', 0))
        if file_size == 0:
            raise ValueError("无法获取文件大小信息")

        # 计算每个线程下载区间
        chunk_size = file_size // thread_count

        # 创建并写入文件
        with open(file_path, 'wb') as file:
            with ThreadPoolExecutor(max_workers=thread_count) as executor:
                tasks = []
                progress_bar = tqdm(total=file_size, unit='iB', unit_scale=True, desc="下载进度")
                
                for index in range(thread_count):
                    start_pos = index * chunk_size
                    end_pos = (index + 1) * chunk_size if index < thread_count - 1 else file_size
                    headers = {'Range': f'bytes={start_pos}-{end_pos - 1}'}
                    tasks.append(executor.submit(
                        download_chunk, url, file, start_pos, end_pos, headers, progress_bar, file_size
                    ))

                # 等待所有任务完成
                for task in tasks:
                    task.result()

                progress_bar.close()

    except requests.exceptions.RequestException as e:
        logging.error(f"文件下载失败,原因:{e}")
        raise
    except Exception as e:
        logging.error(f"发生未知错误:{e}")
        raise

def download_chunk(url, file_obj, start, end, headers, progress_bar, file_size):
    try:
        chunk_response = requests.get(url, headers=headers, stream=True)
        chunk_response.raise_for_status()

        # 写入文件指定位置
        with open(file_obj.name, 'r+b') as file:
            file.seek(start)
            downloaded = 0
            for chunk in chunk_response.iter_content(chunk_size=8192):
                if chunk:
                    file.write(chunk)
                    downloaded += len(chunk)
                    progress_bar.update(len(chunk))

    except requests.exceptions.RequestException as e:
        logging.error(f"分块下载失败({start}-{end}):{e}")
        raise
    except Exception as e:
        logging.error(f"分块写入失败({start}-{end}):{e}")
        raise

def main():
    # 获取用户输入
    download_url = input("请输入下载URL:")
    save_path = input("请输入文件保存路径(例如:/路径/到/文件名,记得加后缀):")
    
    # 自动创建目录
    directory = os.path.dirname(save_path)
    if directory:  # 路径包含目录时处理
        os.makedirs(directory, exist_ok=True)
        logging.info(f"已创建目录:{directory}")

    if not os.path.exists(save_path):
        try:
            download_file(download_url, save_path)
            logging.info("文件下载完成!")
        except Exception as e:
            logging.error(f"下载过程出错:{e}")
    else:
        logging.info("文件已存在,跳过下载")

if __name__ == "__main__":
    main()