创建完成后监视文件夹并复制新文件

Stp*_*111 3 c# filesystemwatcher

我构建了一个控制台应用程序,用于监视 Windows 2019 Server 上的一组文件夹,并使用相同的文件名将任何新创建的 .txt 文件复制到另一个文件夹。到目前为止,它可以实现基本功能。现在我必须处理这样一个事实:大多数时候,这些文件很大,需要几分钟才能完成创建。我已经浏览了几篇 SO 帖子,并拼凑了以下代码来尝试完成此任务:

using System;
using System.IO;

namespace Folderwatch
{
    class Program
    {
        static void Main(string[] args)
        {
            string sourcePath = @"C:\Users\me\Documents\SomeFolder";

            FileSystemWatcher watcher = new FileSystemWatcher(sourcePath);

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

            // Add event handlers.
            watcher.Created += new FileSystemEventHandler(OnCreated);
        }

        // Define the event handlers. 

        private static void OnCreated(object source, FileSystemEventArgs e)
        {
            // Specify what is done when a file is created.
            FileInfo file = new FileInfo(e.FullPath);
            string wctPath = e.FullPath;
            string wctName = e.Name;
            string createdFile = Path.GetFileName(wctName);
            string destPath = @"C:\Users\SomeOtherFolder";
            string sourceFile = wctPath;
            string destFile = Path.Combine(destPath, createdFile);
            WaitForFile(file);
            File.Copy(sourceFile, destFile, true);
        }

        public static bool IsFileLocked(FileInfo file)
        {
            try
            {
                using (FileStream stream = file.Open(FileMode.Open, FileAccess.Read, FileShare.None))
                {
                    stream.Close();
                }
            }
            catch (IOException)
            {
                //the file is unavailable because it is:
                //still being written to
                //or being processed by another thread
                //or does not exist (has already been processed)
                return true;
            }

            //file is not locked
            return false;
        }

        public static void WaitForFile(FileInfo filename)
        {
            //This will lock the execution until the file is ready
            //TODO: Add some logic to make it async and cancelable
            while (!IsFileLocked(filename)) { }
        }

    }
}
Run Code Online (Sandbox Code Playgroud)

我在该方法中尝试执行的操作OnCreated是检查并等待文件创建完成,然后将文件复制到另一个目标。我似乎不知道我在用该WaitForFile(file)行做什么 - 如果我注释掉该行并且文件创建是即时的,文件将按预期复制。如果我使用这WaitForFile条线,就不会发生任何事情。我从其他帖子中获取了IsFileLocked和方法,但显然我没有正确实现它们。WaitForFile

我注意到这个 Powershell 版本创建时复制文件(一旦完成),我不确定这里的答案是否可以为我指明正确的方向,因为我对 PS 的了解甚至不如对 C# 的了解。

编辑#1:在接受答案之前我应该​​测试更长时间 - 我认为我们已经接近了,但是在程序运行大约一分钟后,我在程序崩溃之前收到以下错误:

未处理的异常。System.IO.IOException:进程无法访问文件“C:\Users\me\Dropbox\test1.log”,因为它正在被另一个进程使用。在System.IO.FileSystem.CopyFile(字符串sourceFullPath,字符串destFullPath,布尔覆盖)在Folderwatch.Program.OnCreated(对象源,FileSystemEventArgs e)在C:\Users\me\OneDrive - Development\Source\repos\FolderWatchCG\FolderWatchCG \Program.cs:第 61 行位于 System.Threading.Tasks.Task。<>c.b__139_1(对象状态)位于 System.Threading.QueueUserWorkItemCallbackDefaultContext.Execute()
位于 System.Threading.ThreadPoolWorkQueue.Dispatch() 位于 System.Threading。 _ThreadPoolWaitCallback.PerformWaitCallback()

任何对此的建议将不胜感激。当我进一步分析这些文件夹中的文件时,其中一些是实时写入的日志文件,因此文件可能在实际完成之前就已经写入了几个小时。我想知道其中之一是否NotifyFilter会在这里发挥作用?

Ahm*_*eed 5

该方法有一个错误WaitForFile(),即它当前在文件锁定时等待(而不是相反)。除此之外,您还需要一种方法来确认该文件确实存在。实现这一目标的一个简单方法是将WaitForFile()方法更改为如下所示:

public static bool WaitForFile(FileInfo file)
{
    while (IsFileLocked(file))
    {
        // The file is inaccessible. Let's check if it exists.
        if (!file.Exists) return false;
    }

    // The file is accessible now.
    return true;
}
Run Code Online (Sandbox Code Playgroud)

只要文件存在且无法访问,这就会一直等待。

然后,您可以按如下方式使用它:

bool fileAvailable = WaitForFile(file);
if (fileAvailable)
{
    File.Copy(sourceFile, destFile, true);
}
Run Code Online (Sandbox Code Playgroud)

但这种方法的问题在于循环使while线程保持繁忙,这 a) 消耗了大量的 CPU 资源,b) 阻止程序处理其他文件,直到它完成等待该文件。因此,最好在每次检查之间使用异步等待。

将方法更改WaitForFile为:

public static async Task<bool> WaitForFile(FileInfo file)
{
    while (IsFileLocked(file))
    {
        // The file is inaccessible. Let's check if it exists.
        if (!file.Exists) return false;
        await Task.Delay(100);
    }

    // The file is accessible now.
    return true;
}
Run Code Online (Sandbox Code Playgroud)

OnCreated然后,像这样在里面等待:

private async static void OnCreated(object source, FileSystemEventArgs e)
{
    // ...

    bool fileAvailable = await WaitForFile(file);
    if (fileAvailable)
    {
        File.Copy(sourceFile, destFile, true);
    }
}
Run Code Online (Sandbox Code Playgroud)