【问题标题】:Monitor folder and copy new files once their creation is complete创建完成后监视文件夹并复制新文件
【发布时间】:2020-11-09 16:22:21
【问题描述】:

我构建了一个控制台应用程序,用于监控 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 上的其他帖子中获取了 IsFileLockedWaitForFile 方法,但我显然没有正确实现它们。

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

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

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

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

【问题讨论】:

  • 我看到了一个可能的失败场景——如果你决定复制一个文件并且其他东西开始写入它,你可能会得到这个错误。如果您指出第 61 行在哪里,会更容易弄清楚。
  • 第 61 行是 File.Copy 方法行(请参阅下面来自 41686d6564 的答案中的 OnCreated 方法)

标签: c# filesystemwatcher


【解决方案1】:

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);
    }
}

【讨论】:

  • 非常全面且经过深思熟虑!现在就试试!谢谢!
  • 看起来这很有效。做得好,再次感谢您。
  • 请查看我在原始帖子中的编辑,了解我现在遇到的例外情况。
  • @Stpete111 这很可能是由race condition 引起的(您检查并确认文件未锁定并即将开始复制它> 另一个线程/进程锁定该文件并开始写入它> 你试图开始复制 > 哎呀!),所以你可能不得不将你的 File.Copy() 调用包装在一个 try-catch 中,并决定程序在失败时应该做什么。相关帖子:12
猜你喜欢
  • 2016-03-01
  • 2019-07-02
  • 1970-01-01
  • 2018-11-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多