Python与rsync:如何处理重名文件问题?
解决远程相机会话重名文件的rsync+Python自动化方案
嘿,这个场景我之前帮朋友处理过类似的情况——远程相机重启会话后生成的重名文件确实会给同步和归档带来麻烦。核心思路就是给每个会话的文件加上唯一标识,再用Python自动化整个流程,结合rsync完成上传和外置HDD归档。下面是具体的实现方案:
一、核心逻辑拆解
- 识别会话边界:相机重启应用后,新生成的文件虽然文件名重复,但它们的创建时间会和上一个会话的最后一个文件有明显间隔(比如重启应用的几分钟空档),我们可以用这个时间差来区分不同会话。
- 给会话文件做唯一标记:要么给每个会话创建独立的子目录,要么直接给文件名加上会话ID/时间戳前缀,确保不管文件名怎么重复,整体路径是唯一的。
- Python调用rsync自动化执行:用Python的
subprocess模块调用rsync命令,完成上传到远程服务器、移动到外置HDD的操作,同时还能校验命令执行结果,避免出错。
二、具体实现步骤
1. 监控新文件并区分会话
我们可以用Python监控相机照片的存储目录,当新文件到来时,通过时间差判断是否属于新会话:
- 记录上一个文件的修改时间,如果当前文件和上一个文件的时间差超过阈值(比如5分钟,可根据实际情况调整),则判定为新会话。
- 用时间戳作为会话ID,确保每个会话的标识唯一。
import os import time from datetime import datetime import shutil # 相机照片的原始存储目录 SOURCE_DIR = "/path/to/camera/photos" # 临时归类会话文件的目录 SESSION_BASE_DIR = "/path/to/session/files" # 会话时间间隔阈值(秒),超过这个时间判定为新会话 SESSION_GAP_THRESHOLD = 300 # 5分钟 last_file_mtime = 0 current_session_id = None current_session_dir = None def get_session_id(): # 用当前时间戳作为会话ID,确保全局唯一 return datetime.now().strftime("%Y%m%d_%H%M%S") while True: # 获取目录下的所有JPG文件,按修改时间排序 files = sorted( [f for f in os.listdir(SOURCE_DIR) if f.lower().endswith(".jpg")], key=lambda x: os.path.getmtime(os.path.join(SOURCE_DIR, x)) ) for file in files: file_path = os.path.join(SOURCE_DIR, file) file_mtime = os.path.getmtime(file_path) # 判断是否触发新会话 if file_mtime - last_file_mtime > SESSION_GAP_THRESHOLD or current_session_id is None: current_session_id = get_session_id() current_session_dir = os.path.join(SESSION_BASE_DIR, current_session_id) os.makedirs(current_session_dir, exist_ok=True) print(f"新会话启动:{current_session_id}") # 将文件移动到当前会话专属目录 dest_path = os.path.join(current_session_dir, file) if not os.path.exists(dest_path): shutil.move(file_path, dest_path) print(f"移动文件到会话目录:{file} -> {current_session_dir}") last_file_mtime = file_mtime # 每隔10秒检查一次新文件 time.sleep(10)
2. 调用rsync完成上传和归档
在文件归类到会话目录后,我们可以扩展代码,调用rsync完成远程上传和外置HDD归档:
import subprocess # 远程服务器信息(格式:用户@主机:目标路径) REMOTE_SERVER = "user@remote-host:/path/to/remote/storage" # 外置HDD的存储目录 EXTERNAL_HDD_DIR = "/path/to/external/hdd/storage" def run_rsync_command(cmd): # 执行rsync命令并返回执行结果 try: result = subprocess.run(cmd, shell=True, check=True, capture_output=True, text=True) print(f"命令执行成功:{cmd}") return True except subprocess.CalledProcessError as e: print(f"命令执行失败:{cmd},错误信息:{e.stderr}") return False def process_session(session_dir): # 1. 上传会话目录到远程服务器 upload_cmd = f"rsync -avz {session_dir}/ {REMOTE_SERVER}/{os.path.basename(session_dir)}/" if not run_rsync_command(upload_cmd): return False # 2. 将会话目录移动到外置HDD(--remove-source-files实现移动效果) archive_cmd = f"rsync -avz --remove-source-files {session_dir}/ {EXTERNAL_HDD_DIR}/{os.path.basename(session_dir)}/" if run_rsync_command(archive_cmd): # 删除空的会话目录 os.rmdir(session_dir) print(f"会话归档完成:{session_dir}") return True return False
3. 整合完整流程
你可以把会话监控和处理逻辑整合:当一个会话结束(比如超过阈值时间没有新文件到来),就触发上传和归档操作;或者设置定时任务,每隔一段时间处理已经完成的会话目录,避免占用本地空间。
三、额外优化建议
- 错误重试机制:如果rsync上传失败,可以添加重试逻辑(比如最多重试3次),避免文件同步失败。
- 日志记录:将所有操作记录到日志文件,方便后续排查问题。
- 文件校验:上传时可以添加
--checksum参数,让rsync通过校验和验证文件完整性,确保上传的文件和源文件一致。 - 并发处理:如果同时有多个会话需要处理,可以用多线程同时执行不同会话的上传操作,提升效率(注意控制线程数量,避免占用过多带宽)。
内容的提问来源于stack exchange,提问作者rvrvrv




