在 Armbian 上自动下载和更新 ZIP 文件(带版本检查)
本文介绍如何在 Armbian(基于 Debian 的嵌入式系统)上设置一个自动化脚本,通过定时任务每晚 2:30 检查并下载指定的 ZIP 文件(来自 JSON 配置),并通过版本检查避免重复下载相同的文件。我们以 https://www.252035.xyz/p/jsm.json 中的 ZIP 链接为例,解决 DNS 问题、缓存问题,并使用 GitHub 下载加速前缀。
背景
目标是从 JSON 文件(https://www.252035.xyz/p/jsm.json)中提取 ZIP 下载链接(例如 https://gh.llkk.cc/https://raw.githubusercontent.com/fish2018/PG/main/pg.20251016-1619.zip),并确保:
- 使用固定文件名(current_pg.zip)保存下载文件。
- 将版本信息(例如 20251016-1619)保存到单独文件(version.txt)。
- 仅当版本更新时下载,避免重复下载。
- 使用加速前缀(如 https://git.lishx.dpdns.org/)解决下载问题。
- 防止缓存导致获取旧版本。
环境准备
1. 系统要求
- 操作系统:Armbian(基于 Debian Bookworm 或类似版本)。
- Python:Python 3(通常预装,检查:python3 --version)。
- 依赖:requests库。
- 工具:cron用于定时任务。
2. 安装依赖
由于 Debian 启用 PEP 668(外部管理环境),直接使用 pip 安装可能报错。推荐通过 apt 安装 requests:
apt-get update
apt-get install python3-requests验证:
python3 -c "import requests; print(requests.__version__)"替代方案(虚拟环境):
如果需要特定版本的 requests:
python3 -m venv /opt/zip_updater/venv
source /opt/zip_updater/venv/bin/activate
pip install requests实现脚本
以下是核心脚本,保存为 /opt/zip_updater/update_zip.py:
#!/usr/bin/env python3
import requests
import json
import os
import re
from datetime import datetime
# 配置
JSON_URL = "https://www.252035.xyz/p/jsm.json"
TARGET_DIR = "/opt/zip_updater/downloads/"
CURRENT_ZIP = os.path.join(TARGET_DIR, "current_pg.zip")
VERSION_FILE = os.path.join(TARGET_DIR, "version.txt")
LOG_FILE = os.path.join(TARGET_DIR, "update.log")
ACCELERATE_PREFIX = "https://git.lishx.dpdns.org/"  # GitHub 加速前缀
def log_message(message):
    """记录日志"""
    timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    with open(LOG_FILE, "a", encoding="utf-8") as f:
        f.write(f"[{timestamp}] {message}\n")
    print(f"[{timestamp}] {message}")
def ensure_dir(directory):
    """确保目录存在"""
    os.makedirs(directory, exist_ok=True)
def get_zip_url():
    """从 JSON 中提取 ZIP URL,禁用缓存"""
    try:
        headers = {"Cache-Control": "no-cache", "Pragma": "no-cache"}
        response = requests.get(JSON_URL, timeout=10, headers=headers)
        response.raise_for_status()
        data = response.json()
        for site in data.get("sites", []):
            if site.get("key") == "https://github.com/fish2018/PG":
                zip_url = site.get("zip")
                if zip_url:
                    return zip_url
        return None
    except Exception as e:
        log_message(f"获取 JSON 或提取 URL 失败: {e}")
        return None
def get_direct_github_url(zip_url):
    """从 gh.llkk.cc URL 提取原始 GitHub URL"""
    if "gh.llkk.cc" in zip_url:
        match = re.search(r'https://raw\.githubusercontent\.com/fish2018/PG/main/pg\.\d{8}-\d{4}\.zip', zip_url)
        if match:
            return match.group(0)
    return None
def get_version_from_url(zip_url):
    """从 URL 提取版本号"""
    match = re.search(r'pg\.(\d{8}-\d{4})\.zip', zip_url)
    return match.group(1) if match else None
def read_current_version():
    """读取当前版本号"""
    if os.path.exists(VERSION_FILE):
        with open(VERSION_FILE, "r", encoding="utf-8") as f:
            return f.read().strip()
    return None
def save_version(version):
    """保存版本号到文件"""
    with open(VERSION_FILE, "w", encoding="utf-8") as f:
        f.write(version)
def is_newer_zip(zip_url):
    """检查 ZIP 是否需要更新(基于版本文件)"""
    new_version = get_version_from_url(zip_url)
    if not new_version:
        log_message("无法解析 ZIP URL 日期,强制下载")
        return True, "无法解析 URL 日期"
    
    current_version = read_current_version()
    if current_version is None:
        return True, "无现有版本信息"
    
    if new_version == current_version:
        return False, f"版本 {new_version} 未变化"
    
    try:
        new_date = datetime.strptime(new_version, "%Y%m%d-%H%M")
        current_date = datetime.strptime(current_version, "%Y%m%d-%H%M")
        if new_date > current_date:
            return True, f"发现新版本 {new_version}"
        return False, f"当前版本 {current_version} 已是最新"
    except ValueError:
        log_message("日期解析失败,强制下载")
        return True, "日期解析失败"
def download_zip(zip_url, output_path, max_retries=3):
    """下载 ZIP,支持重定向、加速前缀和防缓存"""
    urls_to_try = []
    direct_url = get_direct_github_url(zip_url)
    if direct_url:
        urls_to_try.append(ACCELERATE_PREFIX + direct_url)  # 优先尝试加速 URL
    urls_to_try.append(zip_url)  # 后尝试原始 URL
    
    headers = {"Cache-Control": "no-cache", "Pragma": "no-cache"}
    for attempt in range(max_retries):
        for url in urls_to_try:
            try:
                log_message(f"尝试下载: {url}")
                response = requests.get(url, timeout=30, stream=True, allow_redirects=True, headers=headers)
                response.raise_for_status()
                
                total_size = int(response.headers.get("content-length", 0))
                downloaded = 0
                with open(output_path, "wb") as f:
                    for chunk in response.iter_content(chunk_size=8192):
                        if chunk:
                            f.write(chunk)
                            downloaded += len(chunk)
                            if total_size > 0:
                                percent = (downloaded / total_size) * 100
                                print(f"下载进度: {percent:.1f}%")
                
                log_message(f"下载成功: {url} -> {output_path}")
                return True
            
            except requests.exceptions.RequestException as e:
                log_message(f"尝试 {attempt + 1} 失败 ({url}): {e}")
                if attempt == max_retries - 1 and url == urls_to_try[-1]:
                    log_message("所有 URL 尝试失败")
                    return False
    return False
def main():
    """主函数:检测并更新"""
    ensure_dir(TARGET_DIR)
    log_message("开始检测 ZIP 更新...")
    
    zip_url = get_zip_url()
    if not zip_url:
        log_message("未找到 ZIP URL,退出")
        return
    
    log_message(f"检测到 ZIP URL: {zip_url}")
    
    should_download, reason = is_newer_zip(zip_url)
    log_message(f"版本检查: {reason}")
    if not should_download:
        log_message("跳过下载")
        return
    
    version = get_version_from_url(zip_url)
    if not version:
        log_message("无法获取版本号,使用默认文件名")
    
    log_message("准备下载新 ZIP...")
    if download_zip(zip_url, CURRENT_ZIP):
        if version:
            save_version(version)
            log_message(f"保存版本号: {version}")
        log_message("更新完成")
    else:
        log_message("更新失败")
if __name__ == "__main__":
    main()脚本功能:
- 固定文件名:下载的 ZIP 保存为 /opt/zip_updater/downloads/current_pg.zip。
- 版本存储:版本号(如 20251016-1619)保存到/opt/zip_updater/downloads/version.txt。
- 版本检查:比较 JSON 中的版本与 version.txt,仅当版本更新时下载。
- 加速前缀:优先使用 https://git.lishx.dpdns.org/下载,解决gh.llkk.ccDNS 问题。
- 防缓存:使用 Cache-Control: no-cache确保获取最新 JSON。
- 日志:记录版本检查、下载进度和结果。
保存和权限:
chmod +x /opt/zip_updater/update_zip.py测试脚本:
python3 /opt/zip_updater/update_zip.py检查 /opt/zip_updater/update.log、version.txt 和 current_pg.zip。
设置定时任务
1. 配置 cron
以 root 用户设置每晚 2:30 执行:
crontab -e添加:
30 2 * * * /usr/bin/python3 /opt/zip_updater/update_zip.py >> /opt/zip_updater/cron.log 2>&1虚拟环境(如果使用):
30 2 * * * /opt/zip_updater/venv/bin/python /opt/zip_updater/update_zip.py >> /opt/zip_updater/cron.log 2>&12. 验证 cron
crontab -l临时测试(每分钟运行):
* * * * * /usr/bin/python3 /opt/zip_updater/update_zip.py >> /opt/zip_updater/cron.log 2>&1检查 /opt/zip_updater/cron.log 和 update.log。
3. 确认时区
timedatectl如需设置为 Asia/Shanghai:
dpkg-reconfigure tzdata验证和调试
1. 测试版本检查
模拟现有版本:
echo "20251016-1619" > /opt/zip_updater/downloads/version.txt
touch /opt/zip_updater/downloads/current_pg.zip
python3 /opt/zip_updater/update_zip.py日志应显示“版本 20251016-1619 未变化,跳过下载”。
2. 测试下载
检查 JSON:
curl -H "Cache-Control: no-cache" https://www.252035.xyz/p/jsm.json测试加速 URL:
curl -L -H "Cache-Control: no-cache" -o test.zip https://git.lishx.dpdns.org/https://raw.githubusercontent.com/fish2018/PG/main/pg.20251016-1619.zip3. 日志检查
- /opt/zip_updater/update.log:记录版本检查、下载进度和结果。
- /opt/zip_updater/cron.log:记录 cron 输出。
故障排除
- 仍下载旧版本: - 检查 version.txt:cat /opt/zip_updater/downloads/version.txt。
- 确认 JSON:curl -H "Cache-Control: no-cache" https://www.252035.xyz/p/jsm.json。
- 如果源站未更新,访问 https://github.com/fish2018/PG/main/确认最新文件名。
 
- 检查 
- DNS 问题: - nslookup git.lishx.dpdns.org echo "nameserver 8.8.8.8" | tee -a /etc/resolv.conf
- 缓存问题: - 确保脚本使用 Cache-Control: no-cache。
- 测试加速 URL:curl -v -H "Cache-Control: no-cache" https://git.lishx.dpdns.org/....
 
- 确保脚本使用 
扩展功能
1. 文件校验
添加 MD5 校验:
import hashlib
def verify_zip(file_path):
    with open(file_path, "rb") as f:
        return hashlib.md5(f.read()).hexdigest()
# 在 download_zip 后调用
log_message(f"ZIP MD5: {verify_zip(CURRENT_ZIP)}")2. 邮件通知
安装 mailutils:
apt-get install mailutils在 main() 末尾:
if should_download and download_zip(zip_url, CURRENT_ZIP):
    os.system(f'echo "ZIP 更新完成: {version}" | mail -s "ZIP 更新通知" your_email@example.com')
elif not should_download:
    os.system(f'echo "ZIP 版本未变化: {reason}" | mail -s "ZIP 更新通知" your_email@example.com')总结
通过以上步骤,你可以在 Armbian 上实现每晚 2:30 自动检查和下载 ZIP 文件,固定保存为 current_pg.zip,版本信息存储在 version.txt,并在版本未变时跳过下载。脚本处理了 DNS 问题(gh.llkk.cc)、缓存问题,并优先使用加速前缀,确保稳定运行。如需进一步优化或调试,请检查日志或提供反馈!
 
    
评论 (0)