首页 > 解决方案 > 创建完成后监视文件夹并复制新文件

问题描述

我构建了一个控制台应用程序,用于监控 Windows 2019 服务器上的一组文件夹,并使用相同的文件名将任何新创建的 .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)) { }
        }

    }
}

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

我已经注意到这个 Powershell 版本Copy File On Creation (一旦完成),我不确定这里的答案是否可以为我指明正确的方向 b/c 我对 PS 的了解甚至比对 C# 的了解还要少。

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

未处理的异常。System.IO.IOException:该进程无法访问文件“C:\Users\me\Dropbox\test1.log”,因为它正被另一个进程使用。在 System.IO.FileSystem.CopyFile(String sourceFullPath, String destFullPath, Boolean overwrite) 在 Folderwatch.Program.OnCreated(Object source, FileSystemEventArgs e) 在 C:\Users\me\OneDrive - Development\Source\repos\FolderWatchCG\FolderWatchCG \Program.cs:System.Threading.Tasks.Task.<>c.b__139_1(Object state) 的第 61 行,System.Threading.QueueUserWorkItemCallbackDefaultContext.Execute()
的 System.Threading.ThreadPoolWorkQueue.Dispatch() 的 System.Threading。 _ThreadPoolWaitCallback.PerformWaitCallback()

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

标签: c#filesystemwatcher

解决方案


该方法有一个错误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;
}

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

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

bool fileAvailable = WaitForFile(file);
if (fileAvailable)
{
    File.Copy(sourceFile, destFile, true);
}

但是这种方法的问题是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;
}

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

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

    bool fileAvailable = await WaitForFile(file);
    if (fileAvailable)
    {
        File.Copy(sourceFile, destFile, true);
    }
}

推荐阅读