[C#] FileSystemWatcher 변경된 이벤트가 두 번 발생합니다

텍스트 파일을 찾고있는 응용 프로그램이 있으며 파일에 변경 사항이 있으면 OnChangedeventhandler를 사용하여 이벤트를 처리하고 있습니다. 나는 사용하고 NotifyFilters.LastWriteTime있지만 여전히 이벤트가 두 번 발생합니다. 코드는 다음과 같습니다.

public void Initialize()
{
   FileSystemWatcher _fileWatcher = new FileSystemWatcher();
  _fileWatcher.Path = "C:\\Folder";
  _fileWatcher.NotifyFilter = NotifyFilters.LastWrite;
  _fileWatcher.Filter = "Version.txt";
  _fileWatcher.Changed += new FileSystemEventHandler(OnChanged);
  _fileWatcher.EnableRaisingEvents = true;
}

private void OnChanged(object source, FileSystemEventArgs e)
{
   .......
}

필자의 경우 OnChanged텍스트 파일을 변경 version.txt하고 저장 하면가 두 번 호출 됩니다.



답변

나는 이것이 FileSystemWatcher클래스 의 잘 알려진 버그 / 기능이라는 것을 두려워합니다 . 이것은 클래스의 문서에서 가져온 것입니다.

특정 상황에서 단일 작성 이벤트가 구성 요소가 처리하는 여러 개의 작성 이벤트를 생성하는 것을 알 수 있습니다. 예를 들어 FileSystemWatcher 구성 요소를 사용하여 디렉토리에서 새 파일 작성을 모니터 한 다음 메모장을 사용하여 파일을 작성하여 테스트하는 경우 단일 파일 만 작성된 경우에도 두 개의 작성 이벤트가 생성 될 수 있습니다. 메모장은 쓰기 프로세스 중에 여러 파일 시스템 작업을 수행하기 때문입니다. 메모장은 파일 내용을 만든 다음 파일 특성을 만드는 배치로 디스크에 씁니다. 다른 응용 프로그램도 같은 방식으로 수행 될 수 있습니다. FileSystemWatcher는 운영 체제 활동을 모니터하므로 이러한 애플리케이션이 실행하는 모든 이벤트가 선택됩니다.

이제이 텍스트는 Created이벤트 에 관한 것이지만 다른 파일 이벤트에도 동일하게 적용됩니다. 일부 응용 프로그램에서는 NotifyFilter속성 을 사용 하여이 문제를 해결할 수 있지만 내 경험에 따르면 때로는 수동 복제 필터링 (핵)을 수행해야한다고합니다.

얼마 전에 FileFileWatcher에 대한 몇 가지 팁이 있는 페이지를 예약했습니다 . 확인하고 싶을 수도 있습니다.


답변

대리인에서 다음 전략을 사용하여 해당 문제를 “수정”했습니다.

// fsw_ is the FileSystemWatcher instance used by my application.

private void OnDirectoryChanged(...)
{
   try
   {
      fsw_.EnableRaisingEvents = false;

      /* do my stuff once asynchronously */
   }

   finally
   {
      fsw_.EnableRaisingEvents = true;
   }
}


답변

해당 파일 의 타임 스탬프를 확인하여 OnChanged에서 복제 된 모든 이벤트를 FileSystemWatcher감지하고 삭제할 수 있습니다 File.GetLastWriteTime. 이렇게 :

DateTime lastRead = DateTime.MinValue;

void OnChanged(object source, FileSystemEventArgs a)
{
    DateTime lastWriteTime = File.GetLastWriteTime(uri);
    if (lastWriteTime != lastRead)
    {
        doStuff();
        lastRead = lastWriteTime;
    }
    // else discard the (duplicated) OnChanged event
}


답변

다음은 이벤트가 두 번 발생하는 것을 막는 데 도움이 된 솔루션입니다.

watcher.NotifyFilter = NotifyFilters.FileName | NotifyFilters.Size;

여기서는 NotifyFilter파일 이름과 크기로만 속성을 설정했습니다 .
watcherFileSystemWatcher의 객체입니다. 이것이 도움이되기를 바랍니다.


답변

내 시나리오는 Linux 서버가있는 가상 머신이 있다는 것입니다. Windows 호스트에서 파일을 개발 중입니다. 호스트의 폴더에서 무언가를 변경하면 모든 변경 사항이 업로드되고 Ftp를 통해 가상 서버에 동기화되기를 원합니다. 이것은 파일에 쓸 때 중복 변경 이벤트를 제거하는 방법입니다 (파일을 포함하는 폴더도 수정하도록 플래그 지정).

private Hashtable fileWriteTime = new Hashtable();

private void fsw_sync_Changed(object source, FileSystemEventArgs e)
{
    string path = e.FullPath.ToString();
    string currentLastWriteTime = File.GetLastWriteTime( e.FullPath ).ToString();

    // if there is no path info stored yet
    // or stored path has different time of write then the one now is inspected
    if ( !fileWriteTime.ContainsKey(path) ||
         fileWriteTime[path].ToString() != currentLastWriteTime
    )
    {
        //then we do the main thing
        log( "A CHANGE has occured with " + path );

        //lastly we update the last write time in the hashtable
        fileWriteTime[path] = currentLastWriteTime;
    }
}

주로 파일 쓰기 시간 정보를 저장하는 해시 테이블을 만듭니다. 그런 다음 해시 테이블에 수정 된 파일 경로가 있고 시간 값이 현재 알려진 파일의 변경 사항과 동일하면 이벤트의 복제본임을 알고 무시합니다.


답변

이 코드로 시도하십시오 :

class WatchPlotDirectory
{
    bool let = false;
    FileSystemWatcher watcher;
    string path = "C:/Users/jamie/OneDrive/Pictures/Screenshots";

    public WatchPlotDirectory()
    {
        watcher = new FileSystemWatcher();
        watcher.Path = path;
        watcher.NotifyFilter = NotifyFilters.LastAccess | NotifyFilters.LastWrite
                               | NotifyFilters.FileName | NotifyFilters.DirectoryName;
        watcher.Filter = "*.*";
        watcher.Changed += new FileSystemEventHandler(OnChanged);
        watcher.Renamed += new RenamedEventHandler(OnRenamed);
        watcher.EnableRaisingEvents = true;
    }



    void OnChanged(object sender, FileSystemEventArgs e)
    {
        if (let==false) {
            string mgs = string.Format("File {0} | {1}",
                                       e.FullPath, e.ChangeType);
            Console.WriteLine("onchange: " + mgs);
            let = true;
        }

        else
        {
            let = false;
        }


    }

    void OnRenamed(object sender, RenamedEventArgs e)
    {
        string log = string.Format("{0} | Renamed from {1}",
                                   e.FullPath, e.OldName);
        Console.WriteLine("onrenamed: " + log);

    }

    public void setPath(string path)
    {
        this.path = path;
    }
}


답변

내 접근 방식은 다음과 같습니다.

// Consider having a List<String> named _changedFiles

private void OnChanged(object source, FileSystemEventArgs e)
{
    lock (_changedFiles)
    {
        if (_changedFiles.Contains(e.FullPath))
        {
            return;
        }
        _changedFiles.Add(e.FullPath);
    }

    // do your stuff

    System.Timers.Timer timer = new Timer(1000) { AutoReset = false };
    timer.Elapsed += (timerElapsedSender, timerElapsedArgs) =>
    {
        lock (_changedFiles)
        {
            _changedFiles.Remove(e.FullPath);
        }
    };
   timer.Start();
}

이것이 메일에서 첨부 파일로 파일을 보내는 프로젝트 에서이 문제를 해결하는 데 사용한 솔루션입니다. 타이머 간격이 짧아도 두 번 발생하는 이벤트를 쉽게 피할 수 있지만 초당 1보다 큰 메시지로 사서함을 채우는 것보다 약간의 변경 사항이 누락되어 행복했기 때문에 1000은 괜찮습니다. 적어도 여러 파일이 동시에 변경되는 경우에는 정상적으로 작동합니다.

내가 생각한 또 다른 해결책은 목록을 해당 MD5에 대한 사전 매핑 파일로 바꾸는 것이므로 항목을 삭제할 필요는 없지만 값을 업데이트 할 필요가 있기 때문에 임의의 간격을 선택할 필요가 없습니다. 변경되지 않은 경우 물건을 취소하십시오. 파일이 모니터링되고 점점 더 많은 메모리를 사용함에 따라 메모리에서 사전이 증가하는 단점이 있지만 모니터링되는 파일의 양이 FSW의 내부 버퍼에 따라 다르므로 중요하지 않을 수도 있습니다. MD5 컴퓨팅 시간이 코드 성능에 어떤 영향을 미치는지 모르겠다.