Python操作OneDrive文件重命名报PermissionError[WinError32]
问题现象
运行Python文件加密脚本时触发Win32权限错误,加密逻辑本身可正常运行、生成加密后的文件,但执行原文件重命名操作时失败,报错信息如下:
<_io.TextIOWrapper name='C:/Users/depen/OneDrive/Documents/PNG_transparency_demonstration_1 >(2).png' mode='r' encoding='cp1252'> read encrypting encrypted encrypted file saved Traceback (most recent call last): File "c:\Users\depen\OneDrive\Documents\python\timer\encrypt.py", line 93, in <module> encrypt(fpath) File "c:\Users\depen\OneDrive\Documents\python\timer\encrypt.py", line 67, in encrypt os.rename(file_fpath, file_fpath + ".encrypted") PermissionError: [WinError 32] The process cannot access the file because it is being used by another process: 'C:/Users/depen/OneDrive/Documents/PNG_transparency_demonstration_1 (2).png' -> 'C:/Users/depen/OneDrive/Documents/PNG_transparency_demonstration_1 (2).png.encrypted'
根因分析
报错本质是文件句柄泄漏,原文件绝大多数情况是被当前Python进程自身占用,并非其他外部进程导致,问题出在代码逻辑本身:
- 分块读取原文件的外层
with open把文件对象赋值给了变量f,但循环里写加密文件的内层with open又把新的文件对象赋值给了同一个变量f,直接覆盖了原文件的句柄引用。外层with块退出时找不到原文件的句柄,就没法正常关闭它,原文件一直被Python进程占用,后续不管是重命名还是删除都会触发占用报错。 - 在
with上下文管理器内部手动调用f.close()、file.close()完全是多余操作,反而会干扰上下文管理器正常的句柄回收逻辑,进一步加剧句柄泄漏问题。 - 贴出的代码最后是
os.remove(file_path),和报错栈里的os.rename逻辑不一致,说明实际运行的代码版本和贴出的版本有偏差,再加上文件存放在OneDrive同步目录,OneDrive同步进程、杀毒软件实时扫描也可能短暂占用文件,提升报错概率。
修复代码
直接按下面的逻辑调整即可,核心是避免变量名冲突、移除无效的手动close调用、减少不必要的文件操作:
fernet = AES(key) def encrypt(file_path): start = time.time() encrypted_path = file_path + ".encrypted" def read_in_chunks(file_object, chunk_size=52428800): while True: data = file_object.read(chunk_size) if not data: break yield data # 读、写文件用不同变量名,彻底避免变量覆盖 with open(file_path, "rb") as src_file: # 加密文件直接输出为最终带.encrypted后缀的文件,不用先写临时文件再改名 with open(encrypted_path, "wb") as enc_file: for piece in read_in_chunks(src_file): encrypted_data = fernet.encrypt(randbytes(16), piece, randbytes(16)) print(time.time() - start) enc_file.write(encrypted_data) # 出了with块,读写文件的句柄会被自动释放,不需要手动调用close # 记录原始文件名 with open("filenames.txt", "a") as log_file: log_file.write(f"{file_path}\n") # 原文件删除/重命名前加简单重试,规避OneDrive、杀毒软件短暂占用 retry_count = 3 for i in range(retry_count): try: # 如果需要保留原文件仅加后缀,就把下面的os.remove换成os.rename逻辑 os.remove(file_path) break except PermissionError: if i == retry_count - 1: raise time.sleep(0.5) cost = time.time() - start print(f"加密耗时: {cost} 秒")
关键调整说明
- 读写文件分别用
src_file、enc_file命名,从根源上避免变量覆盖导致的句柄泄漏 - 把加密文件的打开操作移到循环外,不需要每写一块就打开关闭一次文件,大文件加密速度会明显提升
- 删掉所有
with块里多余的手动close调用,让上下文管理器自己处理句柄回收 - 直接输出最终命名的加密文件,省去临时文件改名的步骤,减少文件操作触发占用的节点
- 加了3次重试逻辑,遇到后台进程短暂占用文件的场景,等待0.5秒再操作,基本能覆盖OneDrive同步、杀毒扫描的常见场景
内容的提问来源于stack exchange,提问作者Dependra Khatri
相关产品推荐
相关产品推荐

