使用File.Move时重复执行引发文件不存在错误的问题
问题:File.Move重复执行导致文件不存在报错
现象
执行文件移动操作时,系统提示源目录中文件不存在,但实际文件已经被移动。排查后发现File.Move被执行了两次;移除File.OpenRead相关代码后,移动功能恢复正常,但业务需要先读取文件内容再执行移动。
读取文件的原始代码
StreamReader reader = null; reader = new StreamReader(File.OpenRead(path+"/"+fileName));
文件移动代码
if (File.Exists(path + "/" + fileName)) { lock (fileMoveLock) { string timeStamp = GetTimestamp(DateTime.Now); Console.WriteLine(timeStamp); string fileNameSlice = fileName.Substring(0, (fileName.Length - 4)); string finalPath = fileNameSlice + "_" + timeStamp + ".txt"; try { File.Move(path + "/" + fileName, Path.Combine(path + "/archive", finalPath)); Console.WriteLine($"File moved: {path + "/" + fileName} to {finalPath}"); } catch (Exception ex) { // Handle any exceptions that may occur during the move operation Console.WriteLine($"Error moving file: {ex.Message}"); } } } else { // Handle the case where the file does not exist return; }
已尝试的无效操作
- 使用后手动关闭/释放流,依然出现
File.Move执行两次的情况 - 添加
Thread.Sleep无效果 - 加锁
fileMoveLock也无法解决问题 - 试过以下写法,但不确定是否属于不良实践:
try { StreamReader reader = null; reader = new StreamReader(File.OpenRead(path + "/" + fileName)); } catch (Exception ex) { return; }
问题分析与解决方案
核心原因
File.OpenRead打开文件后,如果没有正确释放资源,会导致文件句柄未被完全释放,可能触发重复执行的逻辑(比如文件监控事件、重复调用的业务流程),进而导致File.Move被调用两次。第一次移动成功后,第二次调用时源文件已不存在,就会报错。
正确的文件读取方式
必须确保文件流被正确释放,推荐使用using语句(自动释放资源),这是.NET中处理可释放资源的标准做法,完全不属于不良实践:
try { using (StreamReader reader = new StreamReader(File.OpenRead(Path.Combine(path, fileName)))) { // 在这里读取文件内容,例如: string content = reader.ReadToEnd(); // 处理读取到的数据 } } catch (Exception ex) { Console.WriteLine($"读取文件出错:{ex.Message}"); return; }
using语句会在代码块结束时自动调用Dispose(),彻底释放文件句柄,确保文件不再被占用- 用
Path.Combine替代手动拼接路径,避免路径分隔符的兼容问题
额外优化点
- 移除
File.Exists检查,改用捕获异常处理文件不存在的情况(避免检查后到移动前的竞态条件):
lock (fileMoveLock) { string timeStamp = GetTimestamp(DateTime.Now); Console.WriteLine(timeStamp); string fileNameSlice = fileName.Substring(0, fileName.Length - 4); string finalPath = fileNameSlice + "_" + timeStamp + ".txt"; string sourcePath = Path.Combine(path, fileName); string destPath = Path.Combine(path, "archive", finalPath); try { File.Move(sourcePath, destPath); Console.WriteLine($"文件已移动:{sourcePath} -> {finalPath}"); } catch (FileNotFoundException) { Console.WriteLine("源文件不存在,无需移动"); return; } catch (Exception ex) { Console.WriteLine($"移动文件出错:{ex.Message}"); } }
- 确保
fileMoveLock是类级别的静态对象,否则锁无法跨线程生效:
private static readonly object fileMoveLock = new object();
内容的提问来源于stack exchange,提问作者Ctrying
相关产品推荐
相关产品推荐

