如何实现C#4.5控制台程序在3个文件上传完成后自动启动?
实现文件全量上传后的自动导入方案
嗨,针对你遇到的“三个文件全部上传完成后才触发导入程序”的需求,我给你整理了两种靠谱的解决方案,都是基于你现有的技术栈(C# 4.5 + SQL Server 2014)来实现的:
方案一:改进FileSystemWatcher,添加延迟检查与文件完整性验证
FileSystemWatcher本身确实会在文件开始创建时就触发事件,但我们可以通过两个关键优化来解决问题:
- 延迟检查:给文件上传留足写入时间,避免触发时文件还在传输中
- 文件就绪验证:确认文件已经完全写入,无法被其他进程占用
核心实现步骤
- 从
app.config读取配置参数 - 配置FileSystemWatcher监听目标文件夹的创建和大小变化事件
- 事件触发后延迟几秒,再检查三个文件是否都存在且就绪
- 满足条件时启动导入程序,完成后归档文件避免重复处理
代码示例
using System; using System.Configuration; using System.IO; using System.Linq; using System.Threading.Tasks; using System.Diagnostics; class AutoImportWatcher { static void Main(string[] args) { var sourceFolder = ConfigurationManager.AppSettings["SourceFolder"]; if (!Directory.Exists(sourceFolder)) { Console.WriteLine($"Source folder {sourceFolder} does not exist!"); return; } // 初始化文件监视器 var watcher = new FileSystemWatcher(sourceFolder) { IncludeSubdirectories = false, NotifyFilter = NotifyFilters.FileName | NotifyFilters.Size // 监听文件名和大小变化 }; // 绑定事件 watcher.Created += OnFileChanged; watcher.Changed += OnFileChanged; watcher.EnableRaisingEvents = true; Console.WriteLine($"Watching folder: {sourceFolder}"); Console.WriteLine("Press Enter to exit..."); Console.ReadLine(); watcher.Dispose(); } private static async void OnFileChanged(object sender, FileSystemEventArgs e) { // 延迟5秒,给文件上传留时间(可根据文件大小调整) await Task.Delay(TimeSpan.FromSeconds(5)); CheckAndRunImport(sender); } private static void CheckAndRunImport(object sender) { var watcher = (FileSystemWatcher)sender; var sourceFolder = watcher.Path; var requiredFiles = new[] { ConfigurationManager.AppSettings["DisbursementFile"], ConfigurationManager.AppSettings["EmployeeFile"], ConfigurationManager.AppSettings["AmountFile"] }; // 检查所有文件是否存在且已就绪 bool allFilesReady = requiredFiles.All(file => { string filePath = Path.Combine(sourceFolder, file); return File.Exists(filePath) && IsFileFullyUploaded(filePath); }); if (allFilesReady) { // 临时关闭监视器,避免重复触发 watcher.EnableRaisingEvents = false; try { Console.WriteLine("All files detected, starting import..."); // 启动你的导入控制台程序 Process.Start(@"Path\To\Your\ImportProgram.exe"); // 归档文件,防止重复处理 string archiveFolder = ConfigurationManager.AppSettings["ArchiveFolder"]; if (!Directory.Exists(archiveFolder)) Directory.CreateDirectory(archiveFolder); foreach (var file in requiredFiles) { string sourcePath = Path.Combine(sourceFolder, file); string archivePath = Path.Combine(archiveFolder, $"{DateTime.Now:yyyyMMddHHmmss}_{file}"); File.Move(sourcePath, archivePath); Console.WriteLine($"Archived file: {archivePath}"); } // 记录日志 WriteLog("Import completed successfully, files archived."); } catch (Exception ex) { WriteLog($"Import failed: {ex.Message}"); } finally { // 重新开启监视器 watcher.EnableRaisingEvents = true; } } } // 判断文件是否完全上传(能否被独占打开) private static bool IsFileFullyUploaded(string filePath) { try { using (var stream = File.Open(filePath, FileMode.Open, FileAccess.ReadWrite, FileShare.None)) { return true; } } catch (IOException) { // 文件被占用,说明还在上传 return false; } } // 写入日志 private static void WriteLog(string message) { string logPath = ConfigurationManager.AppSettings["LogFile"]; string logEntry = $"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] {message}{Environment.NewLine}"; File.AppendAllText(logPath, logEntry); } }
方案二:定时扫描文件夹(更简单稳定的备选)
如果觉得FileSystemWatcher的事件触发逻辑太敏感,可以用定时扫描的方式,每隔一段时间检查目标文件夹是否满足条件:
代码示例
using System; using System.Configuration; using System.IO; using System.Linq; using System.Threading; using System.Diagnostics; class AutoImportScanner { static void Main(string[] args) { var scanInterval = TimeSpan.FromMinutes(1); // 每分钟扫描一次,可调整 var timer = new Timer(_ => CheckAndRunImport(), null, TimeSpan.Zero, scanInterval); Console.WriteLine("Auto import scanner started. Press Enter to exit..."); Console.ReadLine(); timer.Dispose(); } private static void CheckAndRunImport() { var sourceFolder = ConfigurationManager.AppSettings["SourceFolder"]; var requiredFiles = new[] { ConfigurationManager.AppSettings["DisbursementFile"], ConfigurationManager.AppSettings["EmployeeFile"], ConfigurationManager.AppSettings["AmountFile"] }; bool allFilesReady = requiredFiles.All(file => { string filePath = Path.Combine(sourceFolder, file); return File.Exists(filePath) && IsFileFullyUploaded(filePath); }); if (allFilesReady) { try { Console.WriteLine("All files detected, starting import..."); Process.Start(@"Path\To\Your\ImportProgram.exe"); // 归档文件 string archiveFolder = ConfigurationManager.AppSettings["ArchiveFolder"]; if (!Directory.Exists(archiveFolder)) Directory.CreateDirectory(archiveFolder); foreach (var file in requiredFiles) { string sourcePath = Path.Combine(sourceFolder, file); string archivePath = Path.Combine(archiveFolder, $"{DateTime.Now:yyyyMMddHHmmss}_{file}"); File.Move(sourcePath, archivePath); } WriteLog("Import completed successfully."); } catch (Exception ex) { WriteLog($"Import error: {ex.Message}"); } } } // 复用之前的IsFileFullyUploaded和WriteLog方法 private static bool IsFileFullyUploaded(string filePath) { try { using (var stream = File.Open(filePath, FileMode.Open, FileAccess.ReadWrite, FileShare.None)) { return true; } } catch (IOException) { return false; } } private static void WriteLog(string message) { string logPath = ConfigurationManager.AppSettings["LogFile"]; string logEntry = $"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] {message}{Environment.NewLine}"; File.AppendAllText(logPath, logEntry); } }
关键注意事项
- 避免重复处理:一定要在导入完成后将文件移到Archive文件夹,或者重命名(比如加
.processed后缀) - 日志记录:严格记录每一步操作,方便后续排查问题
- 权限配置:确保运行自动程序的账户有目标文件夹的读写权限,以及启动导入程序的权限
- 延迟时间调整:如果你的文件很大,需要适当延长延迟时间,避免文件还在写入时就触发导入
内容的提问来源于stack exchange,提问作者Naweez
相关产品推荐
相关产品推荐

