import os
import requests
from concurrent.futures import ThreadPoolExecutor
from tqdm import tqdm
def download_range(url, start, end, temp_file, pbar):
headers = {"Range": f"bytes={start}-{end}"}
with requests.get(url, headers=headers, stream=True) as r:
with open(temp_file, "wb") as f:
for chunk in r.iter_content(chunk_size=1024*1024): # 1MB
if chunk:
f.write(chunk)
pbar.update(len(chunk))
def download_file_multithread(url, filename, num_threads=4):
# 获取文件大小
resp = requests.head(url)
file_size = int(resp.headers.get("Content-Length", 0))
if file_size == 0:
raise Exception("无法获取文件大小")
# 分块区间
part_size = file_size // num_threads
ranges = []
for i in range(num_threads):
start = i * part_size
end = (i+1) * part_size - 1 if i < num_threads - 1 else file_size - 1
ranges.append((start, end))
temp_files = [f"{filename}.part{i}" for i in range(num_threads)]
# 多线程下载
with tqdm(total=file_size, unit="B", unit_scale=True, desc=filename) as pbar:
with ThreadPoolExecutor(max_workers=num_threads) as executor:
for i, (start, end) in enumerate(ranges):
executor.submit(download_range, url, start, end, temp_files[i], pbar)
# 合并文件
with open(filename, "wb") as outfile:
for temp_file in temp_files:
with open(temp_file, "rb") as infile:
outfile.write(infile.read())
os.remove(temp_file)
print(f"下载完成: {filename}")
# 示例调用
if __name__ == "__main__":
download_file_multithread(test_url, "test.bin", num_threads=4)
import os
import time
import requests
import threading
from concurrent.futures import ThreadPoolExecutor
import tkinter as tk
from tkinter import ttk
def download_range(url, start, end, temp_file, progress_data):
headers = {"Range": f"bytes={start}-{end}"}
with requests.get(url, headers=headers, stream=True) as r:
with open(temp_file, "wb") as f:
for chunk in r.iter_content(chunk_size=1024 * 256): # 256KB
if chunk:
f.write(chunk)
size = len(chunk)
with progress_data["lock"]:
progress_data["downloaded"] += size
def update_ui(root, progress_bar, label, progress_data, file_size, start_time):
if not progress_data["done"]:
with progress_data["lock"]:
downloaded = progress_data["downloaded"]
percent = downloaded / file_size * 100
elapsed = time.time() - start_time
speed = downloaded / 1024 / 1024 / elapsed if elapsed > 0 else 0
remaining = (file_size - downloaded) / (speed * 1024 * 1024) if speed > 0 else 0
# 更新进度条和文字
progress_bar["value"] = percent
label.config(text=f"{percent:.2f}% {downloaded / 1024 / 1024:.2f}MB / {file_size / 1024 / 1024:.2f}MB | "
f"速度: {speed:.2f} MB/s | 剩余: {remaining:.1f}s")
root.after(500, update_ui, root, progress_bar, label, progress_data, file_size, start_time)
def download_file_multithread(url, filename, num_threads=4):
resp = requests.head(url)
file_size = int(resp.headers.get("Content-Length", 0))
if file_size == 0:
raise Exception("无法获取文件大小")
# 分块区间
part_size = file_size // num_threads
ranges = []
for i in range(num_threads):
start = i * part_size
end = (i + 1) * part_size - 1 if i < num_threads - 1 else file_size - 1
ranges.append((start, end))
temp_files = [f"{filename}.part{i}" for i in range(num_threads)]
import threading
progress_data = {"downloaded": 0, "lock": threading.Lock(), "done": False}
# 启动 Tkinter UI
root = tk.Tk()
root.title("文件下载器")
progress_bar = ttk.Progressbar(root, orient="horizontal", length=400, mode="determinate", maximum=100)
progress_bar.pack(pady=10)
label = tk.Label(root, text="准备下载...")
label.pack()
start_time = time.time()
root.after(500, update_ui, root, progress_bar, label, progress_data, file_size, start_time)
# 下载线程
def run_download():
with ThreadPoolExecutor(max_workers=num_threads) as executor:
futures = []
for i, (start, end) in enumerate(ranges):
futures.append(executor.submit(download_range, url, start, end, temp_files[i], progress_data))
for f in futures:
f.result()
progress_data["done"] = True
# 合并文件
with open(filename, "wb") as outfile:
for temp_file in temp_files:
with open(temp_file, "rb") as infile:
outfile.write(infile.read())
os.remove(temp_file)
label.config(text=f"下载完成: {filename}")
threading.Thread(target=run_download, daemon=True).start()
root.mainloop()
# 示例调用
if __name__ == "__main__":
download_file_multithread(test_url, "test.bin", num_threads=4)