FileSystemWatcher监控目录上传SharePoint遇文件被占用异常求助
解决FileSystemWatcher拖拽多文件时的“文件被占用”异常
这个问题我太熟悉了!FileSystemWatcher的Created事件触发时机其实有点“急性子”——它在文件刚被创建(甚至内容还没完全写入磁盘)的时候就会通知你,这时候文件大概率还被创建它的进程(比如你拖拽文件时的Windows资源管理器)锁着,直接去读取或上传肯定会抛出System.IO.IOException。下面给你几个实用的解决方案:
1. 给上传逻辑加重试机制
最直接有效的办法就是捕捉异常后,等待一小段时间再重试,直到文件可以被访问或者达到重试上限。这里给你写个异步的重试辅助方法示例:
private async Task UploadFileWithRetry(string filePath, int maxRetries = 5, int delayMs = 500) { for (int retryCount = 0; retryCount < maxRetries; retryCount++) { try { // 尝试以排他方式打开文件,确认没有被占用 using (var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.None)) { // 执行SharePoint上传逻辑,比如传入流和文件名 await UploadToSharePoint(fileStream, Path.GetFileName(filePath)); return; // 成功上传就退出重试循环 } } catch (IOException) { // 如果是最后一次重试,就抛出异常;否则等待后重试 if (retryCount == maxRetries - 1) throw; await Task.Delay(delayMs); } } }
你可以在Created事件的处理方法里直接调用这个重试方法,不用同步阻塞事件线程。
2. 结合Changed事件确认文件就绪
Created事件只触发一次,但Changed事件会在文件内容写入过程中多次触发(比如文件大小变化时)。你可以结合这两个事件,或者优先监听Changed事件来判断文件是否已经完全写入。
比如可以记录每个文件的最后修改时间和大小,当连续两次触发Changed事件时,文件的大小和修改时间都没有变化,就说明文件已经就绪了:
private readonly Dictionary<string, (DateTime LastModified, long FileSize)> _fileStatus = new Dictionary<string, (DateTime, long)>(); private async void OnFileChanged(object sender, FileSystemEventArgs e) { var fileInfo = new FileInfo(e.FullPath); if (!fileInfo.Exists) return; var currentStatus = (fileInfo.LastWriteTimeUtc, fileInfo.Length); if (_fileStatus.TryGetValue(e.FullPath, out var lastStatus)) { // 如果两次状态一致,说明文件已经写完 if (lastStatus == currentStatus) { _fileStatus.Remove(e.FullPath); await UploadFileWithRetry(e.FullPath); return; } } _fileStatus[e.FullPath] = currentStatus; }
3. 提前检查文件是否可访问
在上传前先写个辅助方法,检查文件是否能被正常打开,避免直接抛出异常:
private bool IsFileAccessible(string filePath) { try { using (var stream = File.Open(filePath, FileMode.Open, FileAccess.ReadWrite, FileShare.None)) { return stream.Length > 0; // 额外确保文件不是空的 } } catch (IOException) { return false; } }
然后在处理事件时,循环调用这个方法,直到返回true再执行上传:
private async void OnFileCreated(object sender, FileSystemEventArgs e) { while (!IsFileAccessible(e.FullPath)) { await Task.Delay(300); } await UploadToSharePoint(e.FullPath); }
额外注意事项
- 不要在FileSystemWatcher的事件处理线程里做耗时的上传操作,最好把任务放到异步队列里处理,避免阻塞事件线程导致后续事件堆积。
- 拖拽多个文件时,Windows资源管理器可能会逐个锁定文件,所以重试间隔不要设置得太短(300-500毫秒比较合适)。
- 处理文件前一定要先检查文件是否还存在,避免文件在触发事件后被删除或重命名的情况。
内容的提问来源于stack exchange,提问作者Mondblut
相关产品推荐
相关产品推荐

