本文目录导读:

我来帮你写一个增量文件下载脚本,以下是两种常用实现方式:
基于文件大小和修改时间的简单增量下载
import os
import hashlib
import json
import requests
from pathlib import Path
from urllib.parse import urlparse
class IncrementalDownloader:
def __init__(self, download_dir="downloads", state_file=".download_state.json"):
self.download_dir = Path(download_dir)
self.state_file = Path(state_file)
self.state = self._load_state()
self.download_dir.mkdir(exist_ok=True)
def _load_state(self):
"""加载下载状态"""
if self.state_file.exists():
with open(self.state_file, 'r') as f:
return json.load(f)
return {}
def _save_state(self):
"""保存下载状态"""
with open(self.state_file, 'w') as f:
json.dump(self.state, f, indent=2)
def _get_remote_file_info(self, url):
"""获取远程文件信息"""
try:
response = requests.head(url, allow_redirects=True)
response.raise_for_status()
info = {
'size': int(response.headers.get('content-length', 0)),
'last_modified': response.headers.get('last-modified', ''),
'etag': response.headers.get('etag', '')
}
return info
except Exception as e:
print(f"获取远程文件信息失败: {e}")
return None
def _get_local_file_info(self, filepath):
"""获取本地文件信息"""
if not filepath.exists():
return None
return {
'size': filepath.stat().st_size,
'last_modified': filepath.stat().st_mtime
}
def _calculate_file_hash(self, filepath):
"""计算文件哈希值"""
sha256_hash = hashlib.sha256()
with open(filepath, "rb") as f:
for byte_block in iter(lambda: f.read(4096), b""):
sha256_hash.update(byte_block)
return sha256_hash.hexdigest()
def download_file(self, url, filename=None, verify_hash=False):
"""下载单个文件"""
if not filename:
filename = url.split('/')[-1]
local_filepath = self.download_dir / filename
remote_info = self._get_remote_file_info(url)
if not remote_info:
return False
# 检查是否需要增量下载
state_key = url
previous_state = self.state.get(state_key, {})
needs_download = True
if local_filepath.exists():
local_info = self._get_local_file_info(local_filepath)
# 比较文件大小
if local_info and remote_info['size'] == local_info['size']:
# 比较修改时间或ETag
if remote_info.get('etag') and previous_state.get('etag') == remote_info['etag']:
needs_download = False
elif remote_info.get('last_modified') and previous_state.get('last_modified') == remote_info['last_modified']:
needs_download = False
if needs_download:
print(f"正在下载: {filename}")
try:
response = requests.get(url, stream=True)
response.raise_for_status()
# 支持断点续传
if local_filepath.exists() and previous_state.get('partial'):
# 获取已下载的大小
existing_size = local_filepath.stat().st_size
headers = {'Range': f'bytes={existing_size}-'}
response = requests.get(url, headers=headers, stream=True)
mode = 'ab'
print(f"继续下载: {filename} (从 {existing_size} 字节继续)")
else:
mode = 'wb'
with open(local_filepath, mode) as f:
for chunk in response.iter_content(chunk_size=8192):
if chunk:
f.write(chunk)
# 可选:验证文件完整性
if verify_hash:
file_hash = self._calculate_file_hash(local_filepath)
print(f"文件哈希值: {file_hash}")
# 更新状态
self.state[state_key] = {
'etag': remote_info['etag'],
'last_modified': remote_info['last_modified'],
'size': remote_info['size'],
'partial': False,
'timestamp': str(Path(local_filepath).stat().st_mtime)
}
self._save_state()
print(f"下载完成: {filename}")
return True
except Exception as e:
print(f"下载失败 {filename}: {e}")
# 保存部分下载状态
if local_filepath.exists():
self.state[state_key] = {'partial': True, 'size': local_filepath.stat().st_size}
self._save_state()
return False
else:
print(f"文件已是最新: {filename}")
return True
def download_multiple_files(self, file_list, verify_hash=False):
"""下载多个文件"""
results = []
for item in file_list:
if isinstance(item, dict):
url = item.get('url')
filename = item.get('filename')
else:
url = item
filename = None
if url:
success = self.download_file(url, filename, verify_hash)
results.append(success)
return all(results)
基于 Rsync 的增量同步(适合大文件)
import subprocess
import os
from pathlib import Path
class RsyncIncrementalDownloader:
def __init__(self, remote_host=None, remote_port=22, ssh_key=None):
self.remote_host = remote_host
self.remote_port = remote_port
self.ssh_key = ssh_key
def download_using_rsync(self, remote_path, local_path, options=None):
"""使用rsync进行增量下载"""
local_path = Path(local_path)
local_path.mkdir(exist_ok=True, parents=True)
# 构建rsync命令
rsync_cmd = ['rsync', '-avz', '--progress']
# 添加增量更新选项
increment_options = [
'--update', # 跳过已经更新的文件
'--partial', # 保留部分下载的文件
'--inplace', # 原地更新文件(节省空间)
]
rsync_cmd.extend(increment_options)
# 添加自定义选项
if options:
rsync_cmd.extend(options)
# 添加SSH选项
if self.remote_host:
ssh_options = f'ssh -p {self.remote_port}'
if self.ssh_key:
ssh_options += f' -i {self.ssh_key}'
rsync_cmd.extend(['-e', ssh_options])
# 构建远程路径
remote_url = f'{self.remote_host}:{remote_path}'
else:
remote_url = remote_path
rsync_cmd.extend([remote_url, str(local_path)])
print(f"执行命令: {' '.join(rsync_cmd)}")
try:
result = subprocess.run(rsync_cmd, capture_output=True, text=True)
print(result.stdout)
if result.returncode == 0:
print("同步完成!")
return True
else:
print(f"同步失败: {result.stderr}")
return False
except Exception as e:
print(f"执行rsync失败: {e}")
return False
使用示例
# 示例1:使用HTTP增量下载
downloader = IncrementalDownloader(download_dir="my_files")
# 单个文件下载
downloader.download_file("https://example.com/file.zip")
# 多个文件下载
files_to_download = [
{"url": "https://example.com/file1.zip", "filename": "file1.zip"},
{"url": "https://example.com/file2.jpg", "filename": "photo.jpg"},
"https://example.com/file3.pdf" # 自动使用文件名
]
downloader.download_multiple_files(files_to_download)
# 示例2:使用Rsync增量同步
rsync_downloader = RsyncIncrementalDownloader(
remote_host="user@example.com",
remote_port=22,
ssh_key="/path/to/ssh/key"
)
# 同步远程目录到本地
rsync_downloader.download_using_rsync(
"/remote/path/to/files/",
"/local/download/directory/"
)
关键特性
- 增量检测:通过比较文件大小、修改时间、ETag 判断是否需要下载
- 断点续传:支持部分下载文件的续传
- 文件验证:可选的文件哈希验证
- 状态持久化:保存下载状态到JSON文件
- Rsync支持:适合大量文件或大文件的增量同步
选择哪种方案取决于你的具体需求:HTTP下载适合网络文件,Rsync适合目录同步和本地/远程服务器之间的传输。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播