C# - 等待复制操作完成

时间:2011-01-13 17:31:00

标签: c# .net

我有一个作为Windows服务运行的程序,它处理特定文件夹中的文件。

由于它是一项服务,它会不断监视文件夹中是否添加了新文件。该计划的部分工作是对目标文件夹中的文件进行比较,并标记不匹配的文件。

我想要做的是检测正在运行的复制操作以及何时完成,以便在匹配文件尚未被复制到目标文件夹时,文件不会过早地被标记。

我想要做的是使用FileSystemWatcher来查看目标文件夹,看看是否正在进行复制操作。如果有,我将程序的主线程置于休眠状态,直到复制操作完成,然后继续像正常一样对文件夹执行操作。

我只想对这种方法有所了解,看看它是否有效。如果其他人有任何其他独特的方法来解决这个问题,我们将不胜感激。

更新:

我为这个混乱道歉,当我说目标目录时,我指的是包含我想要处理的所有文件的源文件夹。我的程序的一部分功能是将源目录的目录结构复制到目标目录,并将所有有效文件复制到该目标目录,保留原始源目录的目录结构,即用户可以复制包含文件的文件夹到源目录。我希望通过确保将包含更多子文件夹和文件的新文件夹集复制到源目录进行处理来防止错误,我的程序在复制过程完成之前不会开始在目标目录上运行。

4 个答案:

答案 0 :(得分:11)

是的,使用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
};

不幸的是,这是你能做的最好的事情。没有干净的方法可以知道文件已准备好供您使用。

答案 1 :(得分:3)

您正在寻找的是典型的producer/consumer方案。您需要执行的操作在此'Producer/consumer queue'page部分中进行了概述。这将允许您使用多线程(可能跨越后台工作者)来复制文件,这样您就不会阻止主服务线程收听系统事件&你可以在那里执行更有意义的任务 - 比如检查新文件和更新队列。 on main thread do check for new files background threads perform the actual coping task public class File { public string FullPath {get; internal set;} public bool CopyInProgress {get; set;} // property to make sure // .. other properties if desired } 。从个人经验(已经实现了这个任务)来看,除非你在多台CPU机器上运行,但是这个过程非常干净,并且这种方法没有太多的性能提升。顺利+代码在逻辑上很好地分开。

简而言之,您需要做的就是拥有如下对象:

EDIT

然后按照上面发布的教程发出对File对象的锁定&更新它的队列&复制它。使用此方法,您可以使用此type approaches而不是持续监视文件副本完成。 这里要实现的重点是,您的服务每个实际物理文件只有一个File对象实例 - 只需确保您(1)在添加&时锁定队列。删除& (2)初始化更新时锁定实际的File对象。

{{1}} :高于我所说的“这种方法没有太多的性能提升,除非”如果你在单个帖子中采用这种方法,我会参考@Jason的建议由于@Jason的解决方案执行非常昂贵的IO操作,在大多数情况下都会失败,因此这种方法必须明显加快。我还没有测试过,但我很确定,因为我的方法不需要打开IO操作(只有一次),流(只有一次)&关闭文件(仅限一次)。 @Jason方法建议多个开放,开放,开放,开放的操作,全部失败除了最后一个。

答案 2 :(得分:2)

一种方法是尝试打开文件,看看是否收到错误。如果正在复制该文件,则该文件将被锁定。这将以共享模式打开文件,因此它将与文件上已打开的写锁定冲突:

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

另一种方法是检查文件大小。如果要将文件复制到它,它会随着时间而改变。

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

答案 3 :(得分: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);
    }
}