• System.IO.FileSystemWatcher的坑


    🚀 优质资源分享 🚀

    学习路线指引(点击解锁)知识定位人群定位
    🧡 Python实战微信订餐小程序 🧡进阶级本课程是python flask+微信小程序的完美结合,从项目搭建到腾讯云部署上线,打造一个全栈订餐系统。
    💛Python量化交易实战💛入门级手把手带你打造一个易扩展、更安全、效率更高的量化交易系统

    System.IO命名空间下面有一个FileSystemWatcher,这个东西可以实现文件变动的提醒。需要监控文件夹变化(比如FTP服务器)的情形非常适用。

    需要监控文件新建时,我们可以这么写:

    _fileSystemWatcher.Path = path;
    _fileSystemWatcher.IncludeSubdirectories = true;
    _fileSystemWatcher.Created += _fileSystemWatcher_Created;
    _fileSystemWatcher.EnableRaisingEvents = true;
    
    protected async void _fileSystemWatcher_Created(object sender, FileSystemEventArgs e)
    {
    Console.WriteLine(e.FullPath);
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    感觉还是挺方便的吧?接下来就是坑了。

    传输延迟问题

    FileSystemWatcher只要发现文件创建就触发了,大文件或者FTP等需要一段时间才能完成传输的情况下,直接在时间处理程序中处理文件会由于文件不完整导致错误。可惜的是,FileSystemWatcher并没有內建任何机制可以保障文件传输完成再触发Created事件,我们只能靠自己代码保障。

    以下代码运行于.NET 6,Windows 11,Rocky Linux 9

    Windows only方案

    • FileSystemWatcher除了Created,还提供了Changed事件,我们可以先监听Created事件,然后再监控Changed的情况,当文件属性不在变化时,认为是传输完毕了。
      这种方案可行,不过感觉有点太麻烦了,我需要监听两个事件,还需要处理先后顺序,其实我只想知道创建而已…
    • 在Created事件中,使用排他性的文件打开操作
      在File.Open()函数中,有重载可以提供独占的访问,访问不成功,文件会弹出错误。
                //防止文件上传时间过长,导致无法正常识别
                if (!File.Exists(e.FullPath)) return;
                var accessable = false;
                for (int i = 0; i < 5; i++)
                {
                    try
                    {
                        using (File.Open(e.FullPath, FileMode.Open, FileAccess.ReadWrite, FileShare.None))
                        {
                            Console.WriteLine("Break");
                            accessable = true;
                            break;
                        }
                    }
                    catch (Exception)
                    {
                        Console.WriteLine("Loop" + i);
                    }
                    await Task.Delay(3000);
                }
                //文件超时无法读取,失败。
                if (!accessable) return;
    //后续代码
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    运行可以看见这样的输出,说明方案可行。

    Linux与Windows通用方案

    上面的方案似乎已经解决了我们的问题,我兴致勃勃地部署到Linux机器上时却死活无法正常工作,Debug发现Open()这个方法居然可以一次直接通过,看来Linux下的Share不能正常独占这个文件,还得换一个方法。

    protected async void _fileSystemWatcher_Created(object sender, FileSystemEventArgs e)
            {
                //防止文件上传时间过长,导致无法正常识别
                if (!File.Exists(e.FullPath)) return;
                var accessable = false;
                for (int i = 0; i < 5; i++)
                {
                    await Task.Delay(3000);
                    Console.WriteLine("loop" + i);
                    var time1 = File.GetLastWriteTimeUtc(e.FullPath);
                    await Task.Delay(1000);
                    var time2 = File.GetLastWriteTimeUtc(e.FullPath);
                    if (time1 == time2)
                    {
                        accessable = true;
                        break;
                    }
                }
                //文件超时无法读取,失败。
                if (!accessable) return;
    //后续代码
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    我们可以在程序中定时检查文件的最后修改时间,如果相隔一段时间的两次最后修改时间一致的话,那说明文件已经完成了传输,这种方式不依赖于打开操作,并且可以在Windows和Linux下运行。

    为了防止无限循环,设置了超时,如果在指定的时间内无法完成,那么程序直接跳出。

    参考

  • 相关阅读:
    蓝桥杯-递增三元组(三种解法,二分, 双指针, 前缀和)
    Vim实用技巧
    SpringMVC拦截器、异常、其他注解
    Thinkphp5萤火商城B2C小程序源码
    国家强制性灯具安全标准GB7000.1-2015
    spring的自动装配
    [暑假]什么叫做原型?原型链?原型对象? [待补充]
    前端基础入门之JS的call、apply和argument
    【机器学习】朴素贝叶斯概率模型
    工业相机基本知识理解:帧率、带宽(数据接口)、图像数据格式
  • 原文地址:https://blog.csdn.net/u013190417/article/details/127419039