4

我有一个问题:如何确定文件夹是否已完成从一个位置复制到另一个位置?

目前,只要复制目录中的文件,我的 FileSystemWatcher 就会触发几个事件。我想要的是,当该文件夹中的所有文件都已成功复制时触发一个事件。我的代码现在看起来像这样:

static void Main(string[] args)
    {
        String path = @"D:\Music";
        FileSystemWatcher mWatcher = new FileSystemWatcher();
        mWatcher.Path = path;
        mWatcher.NotifyFilter = NotifyFilters.LastAccess;
        mWatcher.NotifyFilter = mWatcher.NotifyFilter | NotifyFilters.LastWrite;
        mWatcher.NotifyFilter = mWatcher.NotifyFilter | NotifyFilters.DirectoryName;
        mWatcher.IncludeSubdirectories = true;
        mWatcher.Created += new FileSystemEventHandler(mLastChange);
        mWatcher.Changed += new FileSystemEventHandler(mLastChange);

        mWatcher.EnableRaisingEvents = true;
        Console.WriteLine("Watching path: " + path);



        String exit;
        while (true)
        {
               exit = Console.ReadLine();
               if (exit == "exit")
                   break;

        }


    }



    private static void mLastChange(Object sender, FileSystemEventArgs e)
    {
        Console.WriteLine(e.ChangeType + " " + e.FullPath);
    }
4

5 回答 5

2

不幸的是,FileSystemWatcher 不会告诉您文件何时完成写入。所以你的选择是...

  1. 当假设没有更多更改发生时,在最后一次写入后设置超时
  2. 让编写应用程序放置一个不同的锁文件,告诉任何其他程序它已经完成。

重新阅读您的问题后......听起来您无法控制其他应用程序。

因此,您将需要某种超时值来确定所有写入何时完成。基本上创建一个计时器,在每个 filesystemwatcher 事件之后重置......当它超时时,你会触发一个表明它已经完成的事件。

这是您如何将其添加到您的代码中的方法...

static void Main(string[] args)
{
    Timer.Interval = 5000; // 5 seconds - change to whatever is appropriate
    Timer.AutoReset = false;
    Timer.Elapsed += TimeoutDone;
    String path = @"D:\Music";
    FileSystemWatcher mWatcher = new FileSystemWatcher();
    mWatcher.Path = path;
    mWatcher.NotifyFilter = NotifyFilters.LastAccess;
    mWatcher.NotifyFilter = mWatcher.NotifyFilter | NotifyFilters.LastWrite;
    mWatcher.NotifyFilter = mWatcher.NotifyFilter | NotifyFilters.DirectoryName;
    mWatcher.IncludeSubdirectories = true;
    mWatcher.Created += new FileSystemEventHandler(mLastChange);
    mWatcher.Changed += new FileSystemEventHandler(mLastChange);

    mWatcher.EnableRaisingEvents = true;
    Console.WriteLine("Watching path: " + path);
    Timer.Start();

    String exit;
    while (true)
    {
        exit = Console.ReadLine();
        if (exit == "exit")
            break;

    }
}

private static Timer Timer = new Timer();

private static void TimeoutDone(object source, ElapsedEventArgs e)
{
    Console.WriteLine("Timer elapsed!");
}

private static void mLastChange(Object sender, FileSystemEventArgs e)
{
    Console.WriteLine(e.ChangeType + " " + e.FullPath);
    if (Timer != null)
    {
        Timer.Stop();
        Timer.Start();
    }
}
于 2013-07-12T18:41:01.763 回答
0

如果目标是本地文件夹,您可以使用文件系统过滤器驱动程序来跟踪文件创建文件关闭事件。知道之前创建的所有文件何时关闭将通知您复制已完成。

于 2013-07-14T16:47:43.473 回答
0

这非常俗气,但过去我通过为 FileSystemWatcher 类创建自定义装饰器来解决这个问题。在内部,它创建一个 FileSystemWatcher 并注册 Created 和 Changed 事件,包装它们,并在文件完成后抛出它自己的 Created 和 Changed 事件,类似于:

private void Watcher_Changed(Object sender, FileSystemEVentArgs e)
    {
        while (true)
        {
            FileStream stream;
            try
            {
                stream = File.Open(e.FullPath, FileMode.Open, FileAccess.ReadWrite, FileShare.None);
                // If this succeeds, the file is finished
                Changed();
            }
            catch (IOException)
            {
            }
            finally
            {
                if (stream != null) stream.Close();
            }
        }
    }

其中一些来自这里的答案。实际上,您不应该使用无限循环。您可能想要添加超时、在检查之间休眠等,但这是一般的想法。

于 2013-07-12T18:56:59.520 回答
0

我创建了一个 Git 存储库,其中包含一个扩展FileSystemWatcher为仅在复制完成时触发事件的类。

下载FileSystemSafeWatcher并将其添加到您的项目中。

然后将其用作正常FileSystemWatcher并监视事件何时触发。

var fsw = new FileExamSystemWatcher(file);
fsw.EnableRaisingEvents = true;
// Add event handlers here
fsw.Created += fsw_Created;
于 2014-04-25T08:22:07.483 回答
0

不幸的是,没有现成的解决方案。但我设计了一个简单的棘手的解决方案来触发(复制完成)事件。

你应该使用计时器。

        FileSystemWatcher fsw = new FileSystemWatcher();
        string fullPath = "";
        DateTime tempTime;
        fsw.Path = @"C:\temp";

        private void startwatching()
        {
            timer1.Start();
        }
        fsw.EnableRaisingEvents = true;
        fsw.Created += Fsw_Created;

        private void Fsw_Created(object sender, FileSystemEventArgs e)
        {
            tempTime = DateTime.Now.AddSeconds(-4);
            fullPath = e.FullPath;
        }

    private void timer1_Tick(object sender, EventArgs e)
    {
        if (fullPath!=string.Empty)
        {
            timer1.Stop();
            if (tempTime >= Directory.GetLastAccessTime(fullPath))
            {
                DirectoryInfo di = new DirectoryInfo(fullPath);
                listBox1.Items.Add("Folder " + di.Name + " finished copying");
                fullPath = string.Empty;
            }
            else
            {
                tempTime = DateTime.Now;
            }
            timer1.Start();
        }
    }
于 2019-08-21T11:40:15.437 回答