使用FileSystemWatcher监测下载文件时Created事件失效仅触发Renamed事件
问题描述
我用FileSystemWatcher监测下载的FBX文件时遇到异常:
- 勾选Created复选框后,新文件下载到监测文件夹完全触发不了Created事件
- 勾选Renamed复选框后,每个下载文件都会触发Renamed事件,但实际硬盘里看不到文件被重命名的操作
我的需求是监测新建的下载FBX文件,将其复制到其他文件夹后删除原文件,但目前监测功能不正常。相关代码如下:
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Text; using System.Windows.Forms; namespace FileSysMon { public partial class frmFileSysMon : Form { public frmFileSysMon() { InitializeComponent(); } //Sample 01: Declarations. private string content = ""; private string last_event_filename = ""; //Sample 02: Close the form private void btnClose_Click(object sender, EventArgs e) { fileMon.EnableRaisingEvents = false; this.Close(); } //Sample 03: Clear Text Content private void btnClear_Click(object sender, EventArgs e) { txtDisplay.Clear(); } //Sample 04: Do it recursively private void chkRecursive_CheckedChanged(object sender, EventArgs e) { if (chkRecursive.Checked == true ) fileMon.IncludeSubdirectories = true; else fileMon.IncludeSubdirectories = false; } //Sample 05: Set the Path to Spy using Folder Browser Dialog private void btnPickFolder_Click(object sender, EventArgs e) { if (FolderB.ShowDialog() == DialogResult.OK) { txtFolderPath.Text = FolderB.SelectedPath; fileMon.Path = txtFolderPath.Text; } } //Sample 06: Enable capturing file system events private void btnMonitor_Click(object sender, EventArgs e) { fileMon.Path = txtFolderPath.Text; fileMon.Filter = "*.fbx"; txtDisplay.AppendText("Monitoring for fbx type files. Started at: " + DateTime.Now.ToString()); txtDisplay.AppendText(Environment.NewLine); fileMon.EnableRaisingEvents = true; } //Sample 07: Enable capturing file system events private void txtFolderPath_TextChanged(object sender, EventArgs e) { if (string.IsNullOrEmpty(txtFolderPath.Text)) chkRecursive.Enabled = false; else chkRecursive.Enabled = true; } //Sample 08: Monitor all the File Events private void fileMon_Changed(object sender, System.IO.FileSystemEventArgs e) { //!under_modification: Avoids repeated consecutive ourput of Files changed per file if (chkModified.CheckState == CheckState.Checked && last_event_filename != e.FullPath) { content = "File Changed: " + e.FullPath + Environment.NewLine; txtDisplay.AppendText(content); } last_event_filename = e.FullPath; } private void fileMon_Created(object sender, System.IO.FileSystemEventArgs e) { if (chkCreated.CheckState == CheckState.Checked) { content = "File Created: " + e.FullPath + Environment.NewLine; txtDisplay.AppendText(content); } last_event_filename = e.FullPath; } private void fileMon_Deleted(object sender, System.IO.FileSystemEventArgs e) { if (chkDeleted.CheckState == CheckState.Checked) { content = "File Deleted: " + e.FullPath + Environment.NewLine; txtDisplay.AppendText(content); } last_event_filename = e.FullPath; } private void fileMon_Renamed(object sender, System.IO.RenamedEventArgs e) { if (chkRenamed.CheckState == CheckState.Checked) { content = "File Renamed: New Name - " + e.FullPath + " Old Name : " + e.OldFullPath + Environment.NewLine; txtDisplay.AppendText(content); } last_event_filename = e.FullPath; } } }
原因分析与解决方案
异常原因
大多数下载工具(浏览器、迅雷等)的下载逻辑是:先创建带临时后缀(比如.part)或隐藏名称的临时文件,下载完成后再将临时文件重命名为最终的.fbx文件。这个过程对用户透明,但FileSystemWatcher会捕捉到最后的重命名操作,所以你看到Renamed事件触发;而Created事件只在临时文件创建时触发,但临时文件后缀不是.fbx,被你的fileMon.Filter = "*.fbx";规则过滤,因此无法感知。
解决办法
方法1:利用Renamed事件处理(最适配下载场景)
既然下载完成的标志是临时文件重命名为.fbx,直接监听Renamed事件即可,代码修改如下:
private void fileMon_Renamed(object sender, System.IO.RenamedEventArgs e) { if (chkRenamed.CheckState == CheckState.Checked && Path.GetExtension(e.FullPath).Equals(".fbx", StringComparison.OrdinalIgnoreCase)) { content = "检测到下载完成的FBX文件:" + e.FullPath + Environment.NewLine; txtDisplay.AppendText(content); // 替换为你的目标文件夹路径 string targetFolder = @"D:\YourTargetDirectory"; if (!Directory.Exists(targetFolder)) { Directory.CreateDirectory(targetFolder); } string destFilePath = Path.Combine(targetFolder, Path.GetFileName(e.FullPath)); // 确保文件未被占用后执行复制删除 try { File.Copy(e.FullPath, destFilePath, true); File.Delete(e.FullPath); txtDisplay.AppendText("文件已复制并删除原文件" + Environment.NewLine); } catch (IOException ex) { txtDisplay.AppendText("操作失败:" + ex.Message + Environment.NewLine); // 可添加重试逻辑,比如延迟2秒后再次尝试 } } last_event_filename = e.FullPath; }
方法2:放宽过滤规则监听Created事件(适合自定义下载场景)
如果坚持用Created事件,需要调整过滤规则并判断文件是否下载完成:
- 修改过滤规则:
fileMon.Filter = "*.*";(或包含临时文件后缀,比如*.fbx;*.part) - 在Created事件中添加文件状态判断:
private void fileMon_Created(object sender, System.IO.FileSystemEventArgs e) { if (chkCreated.CheckState == CheckState.Checked) { // 延迟2秒确保文件写入完成 Task.Delay(2000).ContinueWith(t => { string ext = Path.GetExtension(e.FullPath).ToLower(); if (ext == ".fbx" || ext == ".part") { try { // 尝试独占打开文件,判断是否被占用 using (FileStream fs = File.Open(e.FullPath, FileMode.Open, FileAccess.ReadWrite, FileShare.None)) { if (ext == ".part") return; // 临时文件跳过,等待重命名后处理 // 切换到UI线程更新界面 this.Invoke(new Action(() => { content = "检测到FBX文件:" + e.FullPath + Environment.NewLine; txtDisplay.AppendText(content); })); // 执行复制删除逻辑 string targetFolder = @"D:\YourTargetDirectory"; if (!Directory.Exists(targetFolder)) { Directory.CreateDirectory(targetFolder); } string destFilePath = Path.Combine(targetFolder, Path.GetFileName(e.FullPath)); File.Copy(e.FullPath, destFilePath, true); File.Delete(e.FullPath); this.Invoke(new Action(() => { txtDisplay.AppendText("文件已复制并删除原文件" + Environment.NewLine); })); } } catch (IOException) { this.Invoke(new Action(() => { txtDisplay.AppendText("文件正在下载,稍后重试" + Environment.NewLine); })); } } }); last_event_filename = e.FullPath; } }
注意:FileSystemWatcher事件在非UI线程触发,操作UI必须用
Invoke切换线程,避免报错。
额外注意事项
- FileSystemWatcher可能因文件操作频繁丢失事件,建议结合定时器定期扫描文件夹作为补充
- 处理文件时必须捕获异常,避免因文件被占用导致程序崩溃
- 若需监听子文件夹,确保
fileMon.IncludeSubdirectories = true已正确设置
内容的提问来源于stack exchange,提问作者Daniel Lip
相关产品推荐
相关产品推荐

