Skip to content

Instantly share code, notes, and snippets.

@aweffr
Created June 19, 2025 01:40
Show Gist options
  • Save aweffr/b02d656d072e249f580aea597354e512 to your computer and use it in GitHub Desktop.
Save aweffr/b02d656d072e249f580aea597354e512 to your computer and use it in GitHub Desktop.
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import argparse
import datetime
import os
import sys
import requests
from urllib.parse import urlparse
def download_file(url: str, filename: str = None):
"""
Downloads a file from a given URL with a progress bar.
Args:
url (str): The URL of the file to download.
filename (str, optional): The path to save the file to.
If None, a timestamped filename is generated.
"""
# --- 1. 确定最终的文件名 ---
if filename is None:
try:
# 尝试从 URL 路径中提取扩展名
path = urlparse(url).path
ext = os.path.splitext(path)[1]
# 如果 URL 类似 "https://.../resource" 没有扩展名,或查询参数中有 .mp3
if not ext and '.mp3' in url:
ext = '.mp3'
# 如果还是没有,使用 .bin 作为通用二进制文件后缀
if not ext:
ext = '.bin'
except Exception:
ext = '.mp3' # 最终回退到 .mp3,符合您最初的用例
timestamp = datetime.datetime.now().strftime('%Y-%m-%dT%H-%M-%S')
output_filename = f"{timestamp}{ext}"
else:
output_filename = filename
# --- 2. 执行下载 ---
try:
print(f"URL: {url}")
print(f"Output: {output_filename}")
# 使用 stream=True 进行流式下载
with requests.get(url, stream=True, allow_redirects=True, timeout=30) as r:
# 检查请求是否成功 (HTTP 状态码 200-299)
r.raise_for_status()
# 从响应头获取文件总大小
total_size = int(r.headers.get('content-length', 0))
# 设置块大小 (8KB)
chunk_size = 8192
downloaded_size = 0
print("Downloading...")
with open(output_filename, 'wb') as f:
for chunk in r.iter_content(chunk_size=chunk_size):
f.write(chunk)
downloaded_size += len(chunk)
# --- 3. 绘制进度条 ---
if total_size > 0:
# 计算进度
progress = downloaded_size / total_size
bar_length = 40
filled_length = int(bar_length * progress)
bar = '█' * filled_length + '-' * (bar_length - filled_length)
# 已下载和总大小的可读格式 (MB)
downloaded_mb = downloaded_size / (1024 * 1024)
total_mb = total_size / (1024 * 1024)
# 打印到同一行
sys.stdout.write(f"\rProgress: [{bar}] {progress:.1%} ({downloaded_mb:.2f}MB / {total_mb:.2f}MB)")
sys.stdout.flush()
# 下载完成,换行
print("\n" + "="*50)
print(f"Downloaded '{output_filename}' successfully.")
print("="*50)
# --- 4. 错误处理 ---
except requests.exceptions.HTTPError as e:
print(f"\nError: HTTP request failed with status code {e.response.status_code}")
print(f"URL: {e.request.url}")
except requests.exceptions.RequestException as e:
print(f"\nError: A network-related error occurred: {e}")
except KeyboardInterrupt:
print("\nDownload cancelled by user. Deleting partial file.")
if os.path.exists(output_filename):
os.remove(output_filename) # 删除未下载完的片段文件
sys.exit(1)
except Exception as e:
print(f"\nAn unexpected error occurred: {e}")
def main():
"""主函数,处理命令行参数"""
parser = argparse.ArgumentParser(
description="A simple script to download a file from a URL with a progress bar.",
# 让帮助信息中的换行符生效
formatter_class=argparse.RawTextHelpFormatter
)
parser.add_argument(
"url",
metavar="URL",
type=str,
help="The URL of the file to download."
)
parser.add_argument(
"-o", "--output",
metavar="FILENAME",
type=str,
default=None,
help="Optional. The output filename.\nIf not provided, a timestamp-based name will be generated.\n(e.g., 2025-06-19T10-30-00.mp3)"
)
args = parser.parse_args()
download_file(args.url, args.output)
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment