10

我有一个文件系统观察器,它会在文件被修改时触发一个事件。一旦锁定被删除,我想从该文件中读取。目前,我只是在触发事件后尝试打开文件,当正在复制大文件时,文件锁定会在事件发送后保持一段时间,从而防止文件被打开以进行读取访问。

有什么建议么?

4

1 回答 1

6

这实际上有点麻烦,除非自从我上次处理它以来问题空间发生了显着变化。

最简单的方法是简单地尝试打开文件,捕获结果IOException,如果文件被锁定,则将其添加到队列中以供稍后检查。您不能只尝试处理传入的每个文件,因为在各种情况下都会为同一个文件生成多个事件,因此对每个接收到的事件设置重试循环可能会很快变成一场灾难。您需要将它们排队,并定期检查队列。

这是一个可以帮助您解决此问题的基本类模板:

public class FileMonitor : IDisposable
{
    private const int PollInterval = 5000;

    private FileSystemWatcher watcher;
    private HashSet<string> filesToProcess = new HashSet<string>();
    private Timer fileTimer;  // System.Threading.Timer

    public FileMonitor(string path)
    {
        if (path == null)
            throw new ArgumentNullException("path");

        watcher = new FileSystemWatcher();
        watcher.Path = path;
        watcher.NotifyFilter = NotifyFilters.FileName;
        watcher.Created += new FileSystemEventHandler(FileCreated);
        watcher.EnableRaisingEvents = true;

        fileTimer = new Timer(new TimerCallback(ProcessFilesTimer),
            null, PollInterval, Timeout.Infinite);
    }

    public void Dispose()
    {
        fileTimer.Dispose();
        watcher.Dispose();
    }

    private void FileCreated(object source, FileSystemEventArgs e)
    {
        lock (filesToProcess)
        {
            filesToProcess.Add(e.FullPath);
        }
    }

    private void ProcessFile(FileStream fs)
    {
        // Your code here...
    }

    private void ProcessFilesTimer(object state)
    {
        string[] currentFiles;
        lock (filesToProcess)
        {
            currentFiles = filesToProcess.ToArray();
        }
        foreach (string fileName in currentFiles)
        {
            TryProcessFile(fileName);
        }
        fileTimer.Change(PollInterval, Timeout.Infinite);
    }

    private void TryProcessFile(string fileName)
    {
        FileStream fs = null;
        try
        {
            FileInfo fi = new FileInfo(fileName);
            fs = fi.OpenRead();
        }
        catch (IOException)
        {
            // Possibly log this error
            return;
        }

        using (fs)
        {
            ProcessFile(fs);
        }

        lock (filesToProcess)
        {
            filesToProcess.Remove(fileName);
        }
    }
}

(注意 - 我在这里从记忆中回忆这个,所以它可能并不完美 - 如果它有问题,请告诉我。)

于 2010-02-09T06:12:46.953 回答