2011-01-13 153 views
25

我有一个作为Windows服务运行的程序;它处理特定文件夹中的文件。由于它是一项服务,它会不断监视文件夹以查找已添加的新文件。程序的一部分工作是对目标文件夹中的文件进行比较,并标记不匹配的文件。我想要做的是能够检测复制操作是否正在进行以及何时完成,这样,如果文件尚未被复制到目标文件夹,则文件不会被过早标记。C# - 等待复制操作完成

我在想的是使用FileSystemWatcher观察目标文件夹并查看是否正在进行复制操作。如果有,我把我的程序的主线程休眠,直到复制操作完成,然后继续对文件夹执行正常操作。我只是想了解一下这种方法,看看它是否有效;如果其他人有任何其他独特的方法来解决这个问题,将不胜感激。

UPDATE:

谢谢大家的建议

更新2:

我的混乱道歉,当我说目标目录,我的意思是包含了所有我想要的文件的源文件夹处理。我的程序的一部分功能是将源目录的目录结构复制到目标目录,并将所有有效的文件复制到该目标目录,保留原始源目录的目录结构,即用户可以复制包含文件的文件夹到源目录。我想通过确保包含更多子文件夹和文件的一组新文件夹被复制到源目录中进行处理来防止错误,直到复制过程完成后,我的程序才会开始在目标目录上运行。

+0

+1。这是一个非常好的问题。我还没有想出一种不觉得像黑客的方法。 – David 2011-01-13 17:35:51

+1

这个问题是类似的,有一些很好的答案:http://stackoverflow.com/questions/30074/monitoring-files-how-to-know-when-a-file-is-complete – mfdoran 2011-01-13 17:38:20

回答

2

您在寻找的是典型的producer/consumer方案。你需要做的是'Producer/consumer queue'这部分page。这将允许您使用多线程(可能跨越背景工作)来复制文件,因此您不会阻止主服务线程侦听系统事件&您可以在那里执行更有意义的任务 - 如检查新文件&更新队列。所以on main thread do check for new filesbackground threads perform the actual coping task。根据个人经验(已经实现这个任务),除非你在多CPU机器上运行,否则这种方法没有太多的性能收益,但是这个过程非常干净。+代码在逻辑上很好地分离。

总之,你所要做的就是有一个像下面的对象:

public class File 
{ 
    public string FullPath {get; internal set;} 
    public bool CopyInProgress {get; set;} // property to make sure 
    // .. other properties if desired 
} 

那么上述问题发布文件对象&队列上的锁来更新它&复制本教程以下。使用这种方法,您可以使用这个type approaches而不是持续监视文件复制完成。 在这里认识到的重要一点是,你的服务有按实际物理文件File对象只有一个实例 - 只要确保你(1)锁定您的队列中添加&删除&(2)锁定实际File对象初始化更新时,当。

EDIT:以上,我说,如果你在一个单独的线程这一做法“没有太多的性能提升这一办法,除非”我参考,来比较@杰森的建议这种方法必须是明显更快,因为@ Jason的解决方案执行非常昂贵的IO操作,这在大多数情况下会失败。这我没有测试,但我很确定,因为我的方法不需要IO操作打开(仅限一次),流(仅限一次)&关闭文件(仅限一次)。 @Jason方法建议多开,开,开,开操作,这将全部失效除了最后一个。

10

是的,请使用FileSystemWatcher,但不要关注创建的事件,请注意更改的事件。每次触发后,尝试打开文件。像这样:

var watcher = new FileSystemWatcher(path, filter); 
watcher.Changed += (sender, e) => { 
    FileStream file = null; 
    try { 
     Thread.Sleep(100); // hack for timing issues 
     file = File.Open(
      e.FullPath, 
      FileMode.Open, 
      FileAccess.Read, 
      FileShare.Read 
     ); 
    } 
    catch(IOException) { 
     // we couldn't open the file 
     // this is probably because the copy operation is not done 
     // just swallow the exception 
     return; 
    } 

    // now we have a handle to the file 
}; 

这是关于你可以做的最好的,不幸的是。有没有干净的方式知道该文件已准备好供您使用。

+0

这是一个很值得我怎么样我也处理了它。对我来说,总是感觉到“Hack-ish”,但是+1,因为我没有更好的东西。 – David 2011-01-13 18:51:53

+1

@ivo s:低效?昂贵?谁在乎?它发生在后台线程上,非常难以置信地成为瓶颈。啊。此外,您正在假设他可以控制复制过程。请注意,这不是他的问题。 – jason 2011-01-14 15:30:20

2

一种方法是尝试打开文件并查看是否出现错误。如果文件被复制,该文件将被锁定。这将打开共享模式的文件,所以它会与该文件已打开写锁冲突:

using(System.IO.File.Open("file", FileMode.Open,FileAccess.Read, FileShare.Read)) {} 

另一种方法是检查文件的大小。如果文件正被复制,它会随时间而改变。

也可以获得打开某个文件的所有应用程序的列表,但我不知道这个API。

1

我知道这是一个老问题,但这里是我寻找的答案,只是这个问题后纺制了一个答案。这必须经过很多调整才能从我所从事的工作中去除一些专有权,所以这可能无法直接编译,但它会给你一个想法。这对我很有用:



void BlockingFileCopySync(FileInfo original, FileInfo copyPath) 
{ 
    bool ready = false; 

    FileSystemWatcher watcher = new FileSystemWatcher(); 
    watcher.NotifyFilter = NotifyFilters.LastWrite; 
    watcher.Path = copyPath.Directory.FullName; 
    watcher.Filter = "*" + copyPath.Extension; 
    watcher.EnableRaisingEvents = true; 

    bool fileReady = false; 
    bool firsttime = true; 
    DateTime previousLastWriteTime = new DateTime(); 

    // modify this as you think you need to... 
    int waitTimeMs = 100; 

    watcher.Changed += (sender, e) => 
    { 
     // Get the time the file was modified 
     // Check it again in 100 ms 
     // When it has gone a while without modification, it's done. 
     while (!fileReady) 
     { 
      // We need to initialize for the "first time", 
      // ie. when the file was just created. 
      // (Really, this could probably be initialized off the 
      // time of the copy now that I'm thinking of it.) 
      if (firsttime) 
      { 
       previousLastWriteTime = System.IO.File.GetLastWriteTime(copyPath.FullName); 
       firsttime = false; 
       System.Threading.Thread.Sleep(waitTimeMs); 
       continue; 
      } 

      DateTime currentLastWriteTime = System.IO.File.GetLastWriteTime(copyPath.FullName); 

      bool fileModified = (currentLastWriteTime != previousLastWriteTime); 

      if (fileModified) 
      { 
       previousLastWriteTime = currentLastWriteTime; 
       System.Threading.Thread.Sleep(waitTimeMs); 
       continue; 
      } 
      else 
      { 
       fileReady = true; 
       break; 
      } 
     } 
    }; 

    System.IO.File.Copy(original.FullName, copyPath.FullName, true); 

    // This guy here chills out until the filesystemwatcher 
    // tells him the file isn't being writen to anymore. 
    while (!fileReady) 
    { 
     System.Threading.Thread.Sleep(waitTimeMs); 
    } 
}