背景:
我在oneDrive上存了很多文件,分布在多个文件夹中,也有套了好几层文件夹的情况。我希望每隔一段时间,将oneDrive上的所有文件向移动硬盘上拷贝一份,但是我只想将距离上一次向移动硬盘拷贝的文件相比,发生变化的一部分,也就是只拷贝距离上一次有变化(新增、删除或修改)的文件。但是每一次拷贝时,都手动将所有文件都一一比对太麻烦了,有什么办法能让我快速的将发生了变化的文件同步过去吗?。使用python,定期的同步(比如每10天),并且将每次同步在日志文件中记录下来,包括但不限于同步时间,同步的具体文件,路径等等。
一、同步文件的代码
import os
import shutil
import time
import hashlib
import logging
from datetime import datetime
# 设置日志记录
logging.basicConfig(filename='sync_log.txt', level=logging.INFO,
format='%(asctime)s - %(message)s')
# 配置OneDrive路径和移动硬盘路径
onedrive_path = r'E:\OneDrive\qlu\OneDrive - stu.qlu.edu.cn'
external_drive_path = r'E:\SyncOneDrive\OneDriveBak'
# 用于计算文件的哈希值
def get_file_hash(file_path):
hash_sha256 = hashlib.sha256()
with open(file_path, 'rb') as f:
while chunk := f.read(8192):
hash_sha256.update(chunk)
return hash_sha256.hexdigest()
# 检查文件是否是云端文件
def is_cloud_file(file_path):
# 如果文件存在但大小为0,表示是云端文件尚未下载
return os.path.exists(file_path) and os.path.getsize(file_path) == 0
# 用于同步文件的函数
def sync_files():
retries = 3 # 最大重试次数
for root, dirs, files in os.walk(onedrive_path):
for file in files:
onedrive_file_path = os.path.join(root, file)
relative_path = os.path.relpath(onedrive_file_path, onedrive_path)
external_file_path = os.path.join(external_drive_path, relative_path)
# 如果是云端文件(大小为0),先跳过或尝试下载
if is_cloud_file(onedrive_file_path):
logging.info(f"Cloud file detected, skipping download: {onedrive_file_path}")
continue # 跳过该文件
# 重试逻辑
for attempt in range(retries):
try:
# 检查文件是否有修改或者不存在
if os.path.exists(external_file_path):
# 比较文件的修改时间或哈希值
if os.path.getmtime(onedrive_file_path) > os.path.getmtime(external_file_path):
shutil.copy2(onedrive_file_path, external_file_path) # 拷贝文件
logging.info(f"Updated: {onedrive_file_path} -> {external_file_path}")
elif get_file_hash(onedrive_file_path) != get_file_hash(external_file_path):
shutil.copy2(onedrive_file_path, external_file_path) # 拷贝文件
logging.info(f"Modified: {onedrive_file_path} -> {external_file_path}")
else:
# 如果文件在移动硬盘上不存在,直接拷贝
os.makedirs(os.path.dirname(external_file_path), exist_ok=True)
shutil.copy2(onedrive_file_path, external_file_path)
logging.info(f"New: {onedrive_file_path} -> {external_file_path}")
break # 如果成功复制文件,则跳出重试循环
except PermissionError as e:
logging.warning(f"PermissionError: {onedrive_file_path} is in use. Retrying... {attempt + 1}/{retries}")
time.sleep(2) # 等待2秒后重试
if attempt == retries - 1:
logging.error(f"Failed to copy file after {retries} attempts: {onedrive_file_path}")
# 直接执行同步任务
def job():
sync_files()
logging.info('Sync completed.\n')
job()
二、定时触发同步
为了让Windows每隔10天自动运行你的Python脚本并确保它以管理员权限运行,你可以使用 任务计划程序 (Task Scheduler) 来实现。
2.1创建Python脚本
确保你已经将Python脚本保存好,并确认它能够正常运行(例如:syncOneDrive.py
)。
2.2 打开任务计划程序
- 在Windows搜索框中输入“任务计划程序”,并选择“任务计划程序”应用。
- 在任务计划程序窗口中,选择右侧的“创建基本任务”。
2.3 创建任务
-
设置任务名称和描述:
- 输入任务的名称(例如:“同步OneDrive到移动硬盘”)。
- 可选择添加描述(如:“每10天同步OneDrive上的文件到移动硬盘”)。
-
设置触发条件:
- 选择“每天”,然后点击“下一步”。
- 在“设置”中,选择“重复任务每10天,并设置结束日期(例如:无限重复或设置具体的结束日期)。
-
设置动作:
- 选择“启动程序”,然后点击“下一步”。
- 在“程序/脚本”框中,输入你的Python解释器的路径。例如,如果你使用的是Python 3.9:
C:\Users\用户名\AppData\Local\Programs\Python\Python39\python.exe
- 在“添加参数”框中,输入你的Python脚本路径,例如:
E:\SyncOneDrive\syncOneDrive.py
- 在“起始于”框中,输入Python脚本所在的文件夹路径:
E:\SyncOneDrive\
-
设置权限(管理员权限):
- 在“完成”页之前,点击“打开高级属性”。
- 进入“常规”选项卡,勾选“以最高权限运行”。这将确保任务以管理员权限运行。
- 点击“确定”保存。
-
完成设置:
- 点击“完成”按钮,任务计划程序将会自动保存并开始工作。
2.4 确认任务
完成创建任务后,你可以在任务计划程序的任务库中看到你创建的任务。你可以右键点击它,选择“运行”来手动测试任务是否能够正常运行。
小提示:
- 管理员权限:确保你有管理员权限运行该任务,否则你可能会遇到权限问题,特别是在访问某些目录时(如
C:
盘或系统目录)。 - Python路径:确保任务计划程序中的Python路径是正确的,特别是如果你使用了虚拟环境或多个Python版本。