监视文件夹并在创建完成后复制新文件。
监视文件夹并在创建完成后复制新文件。
我建立了一个控制台应用程序,在Windows 2019服务器上监视一组文件夹,并将任何新创建的.txt文件复制到另一个文件夹,使用相同的文件名。目前它已经能够实现基本功能。现在我需要处理的是,大多数情况下这些文件很大,需要几分钟才能完成创建。我查阅了几篇Stack Overflow的帖子,并拼凑出以下代码来尝试实现这个功能:
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"; // 添加事件处理程序 watcher.Created += new FileSystemEventHandler(OnCreated); } // 定义事件处理程序 private static void OnCreated(object source, FileSystemEventArgs e) { // 指定文件创建时的操作 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) { // 文件不可用,因为它正在被写入、被其他线程处理或者已经存在(已被处理) return true; } // 文件未被锁定 return false; } public static void WaitForFile(FileInfo filename) { // 这将阻塞执行,直到文件准备就绪 // TODO: 添加一些逻辑使其异步和可取消 while (!IsFileLocked(filename)) { } } } }
我在`OnCreated`方法中尝试的是检查并等待文件创建完成,然后将文件复制到另一个目标位置。我似乎不知道如何使用`WaitForFile(file)`这一行 - 如果我注释掉这行并且文件创建是瞬间完成的,那么文件会按预期复制。如果我使用`WaitForFile`这一行,就什么都不会发生。我从Stack Overflow的其他帖子中获取了`IsFileLocked`和`WaitForFile`方法,但显然没有正确实现它们。
我注意到了这个PowerShell版本的答案Copy File On Creation (once complete),我不确定这个答案是否能给我指引,因为我对PowerShell的了解甚至不如对C#的了解。
编辑#1:我应该在接受答案之前测试更长的时间 - 程序运行大约一分钟后,我收到了以下错误,然后程序崩溃:
Unhandled exception. System.IO.IOException: The process cannot access the file 'C:\Users\me\Dropbox\test1.log' because it is being used by another process.
任何关于这个问题的建议将不胜感激。当我进一步分析这些文件夹中的文件时,我发现其中一些是实时写入的日志文件,所以可能在文件实际完成之前,文件被写入了几个小时。我想知道这里是否有`NotifyFilter`的某种作用?
问题的出现原因是WaitForFile()方法的bug,它当前在文件未锁定时等待(而不是相反)。此外,您需要一种确认文件是否存在的方法。实现这个简单的方法是将WaitForFile()方法更改为如下所示:
public static bool WaitForFile(FileInfo file) { while (IsFileLocked(file)) { // 文件无法访问。让我们检查它是否存在。 if (!file.Exists) return false; } // 文件现在可以访问。 return true; }
这将在文件存在且不可访问时一直等待。
然后,可以按以下方式使用它:
bool fileAvailable = WaitForFile(file); if (fileAvailable) { File.Copy(sourceFile, destFile, true); }
这种方法的问题是while循环会使线程一直忙碌,这会消耗大量的CPU资源,并且阻止程序在等待某个文件期间处理其他文件。因此,最好在每次检查之间使用异步等待。
将WaitForFile方法更改为:
public static async TaskWaitForFile(FileInfo file) { while (IsFileLocked(file)) { // 文件无法访问。让我们检查它是否存在。 if (!file.Exists) return false; await Task.Delay(100); } // 文件现在可以访问。 return true; }
然后,在OnCreated内部使用await:
private async static void OnCreated(object source, FileSystemEventArgs e) { // ... bool fileAvailable = await WaitForFile(file); if (fileAvailable) { File.Copy(sourceFile, destFile, true); } }
这个方法非常全面和深思熟虑!现在尝试一下!谢谢!
看起来这个方法很有效。干得好,再次感谢。
请查看我在原帖中的修改,我现在遇到了一个异常。
这很可能是由于竞态条件引起的(您检查并确认文件未锁定,并且即将开始复制它 > 另一个线程/进程锁定该文件并开始写入 > 您尝试开始复制 > 哎呀!),所以您可能需要在try-catch中包装File.Copy()调用,并决定在失败时程序应该做什么。相关帖子:1,2。