You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

含FileSystemWatcher的Windows服务启动后异常停止问题排查

问题分析与解决方案

核心原因

FileSystemWatcher的Created事件触发时机是文件目录条目刚被创建时,而非文件完全写入、关闭后。摄像头录制视频时,通常会先创建一个文件(甚至是临时文件),持续写入内容直到录制结束,期间可能还会对文件进行重命名、临时删除等操作。这就导致你的addFile方法触发时,事件里的文件路径可能已经失效(比如临时文件被删除、文件还没完全写入就被改名),哪怕你没直接操作文件,只要代码中涉及到验证该路径的文件存在性、读取文件元数据等操作,就会抛出FileNotFoundException。

解决办法

1. 延迟+重试机制

收到Created事件后,先延迟一段时间等待文件稳定,同时加入重试逻辑,避免单次失败就终止服务:

Private Async Sub addFile(sender As Object, e As FileSystemEventArgs)
    Dim maxRetries As Integer = 3
    Dim delayMs As Integer = 1000
    Dim success As Boolean = False

    For retry = 1 To maxRetries
        Await Task.Delay(delayMs * retry) ' 每次重试延迟时间递增
        Try
            ' 先验证文件是否存在
            If Not File.Exists(e.FullPath) Then Continue For
            
            ' 你的数据库写入逻辑
            Dim expireDate As Date = Date.Now.AddDays(7) ' 示例:7天后过期
            Using conn As New SQLiteConnection("Data Source=VideoFiles.db;Version=3;")
                conn.Open()
                Dim cmd As New SQLiteCommand(
                    "INSERT INTO VideoRecords (FilePath, ExpireDate) VALUES (@path, @expire)",
                    conn
                )
                cmd.Parameters.AddWithValue("@path", e.FullPath)
                cmd.Parameters.AddWithValue("@expire", expireDate)
                cmd.ExecuteNonQuery()
            End Using
            
            success = True
            Exit For
        Catch ex As FileNotFoundException
            ' 捕获文件不存在异常,继续重试
            EventLog.WriteEntry("VideoCleanupService", $"Retry {retry}: File {e.FullPath} not found", EventLogEntryType.Warning)
        Catch ex As Exception
            ' 处理其他异常(如数据库错误)
            EventLog.WriteEntry("VideoCleanupService", $"Error processing {e.FullPath}: {ex.Message}", EventLogEntryType.Error)
            Exit Sub
        End Try
    Next

    If Not success Then
        EventLog.WriteEntry("VideoCleanupService", $"Failed to process {e.FullPath} after {maxRetries} retries", EventLogEntryType.Error)
    End If
End Sub

2. 监控Renamed事件

部分摄像头会先写入临时文件(比如temp_xxx.dat),录制完成后重命名为正式视频文件(比如20240520_1230.mp4)。这种场景下,Created事件只会触发临时文件,而你需要的是最终的正式文件路径,因此可以同时监控Renamed事件:

' 初始化时添加Renamed事件监听
watcher.Renamed += AddressOf OnFileRenamed

Private Async Sub OnFileRenamed(sender As Object, e As RenamedEventArgs)
    ' 复用上面的延迟重试逻辑处理最终的文件路径
    Await ProcessVideoFile(e.FullPath)
End Sub

' 把数据库写入逻辑抽成独立方法,方便复用
Private Async Function ProcessVideoFile(filePath As String) As Task
    ' 这里放延迟、重试和数据库写入代码,和上面的addFile逻辑一致
End Function

3. 验证文件可访问性

除了检查File.Exists,还可以尝试以只读模式打开文件,确认文件未被摄像头占用:

Private Function IsFileAccessible(filePath As String) As Boolean
    Try
        Using fs As New FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.None)
            Return True
        End Using
    Catch ex As IOException
        ' 文件被占用或无法访问
        Return False
    Catch ex As FileNotFoundException
        Return False
    End Try
End Function

在处理前调用这个方法,只有返回True时才执行数据库写入。

注意事项

  • 根据摄像头的录制时长调整延迟和重试次数,比如短时长视频可以设置1-2秒延迟,长视频适当延长。
  • Windows服务中使用Async/Await时,确保事件处理方法标记为Async,避免阻塞服务主线程。
  • 务必在异常捕获中记录详细日志,方便后续排查问题。

内容的提问来源于stack exchange,提问作者Nuggzy

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 04:47:25