You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用shutil.copy/copy2复制打开文件时触发[Errno 13]权限拒绝异常

文件备份时无法复制打开状态文件的问题

问题情况

开发小型备份软件,监听文件modified和created事件,多数场景正常,但遇到以下异常:

  • 创建Word文件可正常备份
  • 打开Word文件修改保存后,触发权限错误:[Errno 13] Permission denied

用到的代码片段:

if os.path.isfile(event.src_path):
    if os.path.exists(destinationFilePath) and event.event_type == 'modified':
        os.remove(destinationFilePath)
    
    #We need a short sleep, because the file may be in use by windows creation routine
    time.sleep(1)
    shutil.copy2(event.src_path, destinationFilePath)
    logging.info(f"File created: {destinationFilePath}")

尝试过shutil.copy、shutil.copy2以及subprocess调用系统copy命令,均出现相同权限拒绝问题。

解决方案

1. 增加重试与指数退避

Windows下文件保存后可能被进程短暂锁定,固定1秒sleep不足以覆盖所有情况,改用带重试的逻辑:

import time
import shutil
import logging

def copy_with_retry(src, dest, max_retries=5, initial_delay=1):
    delay = initial_delay
    for attempt in range(max_retries):
        try:
            if os.path.exists(dest):
                os.remove(dest)
            shutil.copy2(src, dest)
            logging.info(f"Successfully copied: {dest}")
            return True
        except PermissionError:
            if attempt < max_retries - 1:
                time.sleep(delay)
                delay *= 2  # 指数退避,每次等待时间翻倍
            else:
                logging.error(f"Failed to copy {src} after {max_retries} attempts")
                return False

# 替换原代码中的复制部分
if os.path.isfile(event.src_path):
    copy_with_retry(event.src_path, destinationFilePath)

2. 使用Windows卷影复制服务(VSS)

如果文件被长期占用,普通复制方法无法读取,可以通过VSS创建磁盘快照,从快照中复制文件。需要安装pywin32库:

import win32com.client
import shutil
import os

def copy_locked_file(src, dest):
    # 初始化VSS客户端
    vss_client = win32com.client.Dispatch("VssClient.VssClient")
    vss_client.Initialize()
    
    try:
        # 创建快照集并添加目标路径的快照
        snapshot_set = vss_client.CreateSnapshotSet()
        snapshot = snapshot_set.AddSnapshot(os.path.dirname(src))
        snapshot_set.Prepare()
        snapshot_set.Commit()
        
        # 获取快照的设备路径,拼接成快照中的文件路径
        snapshot_device = snapshot.GetSnapshotDeviceObject()
        snapshot_src = os.path.join(snapshot_device, os.path.basename(src))
        
        # 从快照复制文件
        if os.path.exists(dest):
            os.remove(dest)
        shutil.copy2(snapshot_src, dest)
        logging.info(f"Copied locked file from snapshot: {dest}")
    finally:
        # 清理VSS资源
        vss_client.Cleanup()

3. 检测文件是否被锁定后再复制

使用psutil库检测文件是否被其他进程占用,等待释放后再执行复制:

import psutil
import time
import shutil
import logging

def is_file_locked(file_path):
    for proc in psutil.process_iter(['pid', 'name']):
        try:
            for handle in proc.open_files():
                if handle.path == file_path:
                    return True
        except (psutil.NoSuchProcess, psutil.AccessDenied):
            continue
    return False

# 修改原代码逻辑
if os.path.isfile(event.src_path):
    # 等待文件解锁
    while is_file_locked(event.src_path):
        time.sleep(1)
    
    # 执行复制
    if os.path.exists(destinationFilePath) and event.event_type == 'modified':
        os.remove(destinationFilePath)
    shutil.copy2(event.src_path, destinationFilePath)
    logging.info(f"File created: {destinationFilePath}")

内容的提问来源于stack exchange,提问作者Daniel Müller

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.10 00:50:16