无法访问该文件,因为它正在被使用

Cannot acces the file because it is being used

我需要使用 FileSystemWatcher 将操作从 dir1 同步到 dir2。当我在 dir1 中创建一个新文件时,相同的文件必须在 dir2 中,如果我 delete/rename/move,在 dir2 中也应该。
当我在 dir1 中创建文件时,它也在 dir2 中创建,但是当我尝试在 dir1 中重命名文件时,出现此异常:未处理的异常。 System.IO.IOException: 进程无法访问文件 'C:\Users\artio\Desktop\FASassignment\root\dir1\g.txt' 因为它正被另一个进程使用。 正如我在谷歌上搜索的那样,这个错误可能会出现,因为流没有关闭,但我没有知道我是否和一个人一起工作。另外,当我在其中创建一个子文件夹和一个文件时,我收到一条错误消息,指出找不到路径并且在 dir2 中它没有创建,你能帮我解决这两个错误吗?提前致谢!
程序:

public class Program2
{
    public static void Main(string[] args)
    {
        string sourcePath = @"C:\Users\artio\Desktop\FASassignment\root\dir1";
        string destinationPath = @"C:\Users\artio\Desktop\FASassignment\root\dir2";

        var source = new DirectoryInfo(sourcePath);
        var destination = new DirectoryInfo(destinationPath);

        using var sourceWatcher = new FileSystemWatcher(sourcePath);

        sourceWatcher.NotifyFilter = NotifyFilters.Attributes
                             | NotifyFilters.CreationTime
                             | NotifyFilters.DirectoryName
                             | NotifyFilters.FileName
                             | NotifyFilters.LastAccess
                             | NotifyFilters.LastWrite
                             | NotifyFilters.Security
                             | NotifyFilters.Size;

        sourceWatcher.Changed += OnChanged;
        sourceWatcher.Created += OnCreated;
        sourceWatcher.Deleted += OnDeleted;
        sourceWatcher.Renamed += OnRenamed;
        sourceWatcher.Error += OnError;

        sourceWatcher.Filter = "*.txt";
        sourceWatcher.IncludeSubdirectories = true;
        sourceWatcher.EnableRaisingEvents = true;

        Console.WriteLine("Press enter to exit.");
        Console.ReadLine();
    }

    private static void OnChanged(object sender, FileSystemEventArgs e)
    {
        if (e.ChangeType != WatcherChangeTypes.Changed)
        {
            return;
        }

        Console.WriteLine($"Changed: {e.FullPath}");
    }

    private static void OnCreated(object sender, FileSystemEventArgs e)
    {
        string value = $"Created: {e.FullPath}";

        string destinationPath = @"C:\Users\artio\Desktop\FASassignment\root\dir2";
        var name = e.Name;
        var fullPath = e.FullPath;
        string destination = Path.Combine(destinationPath, name);
        File.Copy(fullPath, destination,true);

        Console.WriteLine(value);
    }

    private static void OnDeleted(object sender, FileSystemEventArgs e)
    {
        Console.WriteLine($"Deleted: {e.FullPath}");

        var name = e.Name;
        string destinationPath = @"C:\Users\artio\Desktop\FASassignment\root\dir2";
        string destination = Path.Combine(destinationPath, name);
        File.Delete(destination);
    }

    private static void OnRenamed(object sender, RenamedEventArgs e)
    {
        var oldFullPath = e.OldFullPath;
        var fullPath = e?.FullPath;
        Console.WriteLine($"Renamed:");
        Console.WriteLine($"    Old: {e.OldFullPath}");
        Console.WriteLine($"    New: {e.FullPath}");
        string destinationPath = @"C:\Users\artio\Desktop\FASassignment\root\dir2";

        string destination = Path.Combine(destinationPath, fullPath);
        File.Copy(fullPath, destination, true);
        File.Delete(oldFullPath);
    }

    private static void OnError(object sender, ErrorEventArgs e) =>
        PrintException(e.GetException());

    private static void PrintException(Exception? ex)
    {
        if (ex != null)
        {
            Console.WriteLine($"Message: {ex.Message}");
            Console.WriteLine("Stacktrace:");
            Console.WriteLine(ex.StackTrace);
            Console.WriteLine();
            PrintException(ex.InnerException);
        }
    }
}

你的问题是这一行:

string destination = Path.Combine(destinationPath, fullPath);

调试一下,你会发现这并不是你想的那样。您正在尝试合并两个完整路径。

改为使用:

private void OnRenamed(object sender, RenamedEventArgs e)
{                       
    Console.WriteLine($"Renamed:");
    Console.WriteLine($"    Old: {e.OldFullPath}");
    Console.WriteLine($"    New: {e.FullPath}");

    var oldFileName = Path.GetFileName(e.OldFullPath);
    var newFileName = Path.GetFileName(e.FullPath);
    var oldDestinationPath = Path.Combine(Destination, oldFileName);
    var newDestinationPath = Path.Combine(Destination, newFileName);
    var info = new FileInfo(oldDestinationPath);            
    info.MoveTo(newDestinationPath);
}

注意我没有使用 File class。众所周知,这是有问题的。

此外,我建议使用某种进程日志并重试,以便您可以恢复。否则,这可能会留下很多更改,因为文件可能会保留它们,这可能会导致您的操作失败。

更新:

关于您关于其他文件的其他问题,看起来您正试图在创建文件的进程释放其句柄之前访问该文件,因此您可以尝试引入一秒左右的小延迟:

private void OnCreated(object sender, FileSystemEventArgs e)
{
    var name = e.Name;
    var fullPath = e.FullPath;
    string destination = Path.Combine(Destination, name);
    Console.WriteLine($"Copy to: {destination}");
    Thread.Sleep(1000); //Intentional delay
    File.Copy(fullPath, destination, true);
    Console.WriteLine("File copied successfully.");
}