Usando FileSystemWatcher para monitorar um diretório

101

Estou usando um aplicativo Windows Forms para monitorar um diretório e mover os arquivos inseridos nele para outro diretório.

No momento, ele irá copiar o arquivo para outro diretório, mas quando outro arquivo for adicionado, ele simplesmente terminará sem nenhuma mensagem de erro. Às vezes, ele copia dois arquivos antes de terminar no terceiro.

É porque estou usando um aplicativo de formulário do Windows em vez de um aplicativo de console? Existe uma maneira de impedir o programa de terminar e continuar observando o diretório?

private void watch()
{
  this.watcher = new FileSystemWatcher();
  watcher.Path = path;
  watcher.NotifyFilter = NotifyFilters.LastAccess | NotifyFilters.LastWrite
                         | NotifyFilters.FileName | NotifyFilters.DirectoryName;
  watcher.Filter = "*.*";
  watcher.Changed += OnChanged;
  watcher.EnableRaisingEvents = true;
}

private void OnChanged(object source, FileSystemEventArgs e)
{
  //Copies file to another directory.
}

public void Dispose()
{
  // avoiding resource leak
  watcher.Changed -= OnChanged;
  this.watcher.Dispose();
}
homem queijeiro
fonte

Respostas:

144

O problema eram os filtros de notificação. O programa estava tentando abrir um arquivo que ainda estava sendo copiado. Removi todos os filtros de notificação, exceto LastWrite.

private void watch()
{
  FileSystemWatcher watcher = new FileSystemWatcher();
  watcher.Path = path;
  watcher.NotifyFilter = NotifyFilters.LastWrite;
  watcher.Filter = "*.*";
  watcher.Changed += new FileSystemEventHandler(OnChanged);
  watcher.EnableRaisingEvents = true;
}
homem queijeiro
fonte
6
Olá, estava usando essa abordagem, mas quando copio um arquivo, o evento é gerado duas vezes: uma vez quando o arquivo é criado vazio (a cópia começa) e mais uma vez quando a cópia termina. Como evitar esse evento duplicado, algum filtro capaz de tratá-lo sem um controle customizado disso?
dhalfageme
@dhalfageme Eu verifico em ambos os eventos se algo significativo para o meu aplicativo aparece na pasta.
Eftekhari
30

Você não forneceu o código de manipulação de arquivo, mas presumo que cometeu o mesmo erro que todos cometem ao escrever tal coisa pela primeira vez: o evento filewatcher será gerado assim que o arquivo for criado. No entanto, levará algum tempo para que o arquivo seja concluído. Pegue um tamanho de arquivo de 1 GB, por exemplo. O arquivo pode ser criado por outro programa (Explorer.exe copiando-o de algum lugar), mas levará minutos para concluir o processo. O evento é gerado no momento da criação e você precisa esperar que o arquivo esteja pronto para ser copiado.

Você pode esperar que um arquivo esteja pronto usando esta função em um loop.

nvoigt
fonte
25

O motivo pode ser que o observador é declarado como variável local para um método e é coletado como lixo quando o método termina. Você deve declará-lo como um membro da classe. Experimente o seguinte:

FileSystemWatcher watcher;

private void watch()
{
  watcher = new FileSystemWatcher();
  watcher.Path = path;
  watcher.NotifyFilter = NotifyFilters.LastAccess | NotifyFilters.LastWrite
                         | NotifyFilters.FileName | NotifyFilters.DirectoryName;
  watcher.Filter = "*.*";
  watcher.Changed += new FileSystemEventHandler(OnChanged);
  watcher.EnableRaisingEvents = true;
}

private void OnChanged(object source, FileSystemEventArgs e)
{
  //Copies file to another directory.
}
user1912419
fonte
18
watchervariável é mantida ativa (não é coletada como lixo) porque ele se inscreveu no evento Changed.
adospace
1
Acredito que seja porque EnableRaisingEvents está definido como true. Não acho que o status dos manipuladores de eventos do membro tenha a ver com a coleta de lixo. Acho que EnableRaisingEvents tem, o que posso chamar favoravelmente, um comportamento especial neste caso.
Matias Grioni