Monitorando uma pasta

15 respostas
S

Olá, eu preciso criar um programa java que monitore uma pasta à minha escolha, e perceba quando eu crio ou altero um arquivo dentro dela. Eu achei pela net que existe uma classe FileSystemWatcher que faz isso, mas só funciona em Windows mas eu precisava que funcionasse em todos os SO.

Alguem sabe como eu posso fazer isso?

Obrigado desde já.

15 Respostas

I

Olá!

Seu problema é dependente do OS - neste caso, vai ter que implementar uma solução para cada OS que for utilizar (a não ser que você consiga monitorar pela data etc).
Talvez seja necessário utilizar libraries nativas de cada plataforma (dll, so) e capturar o evento da pasta…

T

Sabatt:
mas eu precisava que funcionasse em todos os SO.

“Funcione em todos os SOs”? Isso não existe em Java, mesmo usando apenas métodos dos pacotes java.* (nem os pacotes javax.* têm garantias de funcionarem em todos os SOs".

Reduza um pouco seu escopo (por exemplo, “quero que funcione em Windows, Linux c/ kernel 2.6 e Solaris 10”) e veja o que é possível fazer em cada um.

(Por exemplo, em Windows você pode usar uma biblioteca nativa, como é o caso do tal FileSystemWatcher que você encontra na Internet, e em Linux talvez você ache algo semelhante).

Você pode ver se no Java 7 isso vai estar implementado, caso você possa usar uma versão beta do Java 7 (disponível em http://download.java.net/jdk7 ). Veja o pacote java.nio.file : http://download.java.net/jdk7/docs/api/java/nio/file/package-summary.html

S

Realmente eu me expressei mal. Não precisa ser todos mas os principais…

Pq minha empresa tem um aplicativo que faz isso, só que ele está escrito em Delphi, e a gente tava querendo mudar para Java. E eu precisava descobrir um jeito de monitorar uma pasta pelo menos no windows e no linux, mas a solução que eu achei só funciona no windows.

Utilizar uma solução para cada SO com dll’s parece ser meio complicado… Mas obrigado pelas respostas :slight_smile:

L

Cara, eu tenho algo que talvez possa ajudar.

Coloquei em anexo os dois arquivos principais para isso.
Descrito abaixo, temos o metodo main que está dentro do arquivo DirPoolingController.

Inicialmente instanciamos a classe DirPoolingController e passamos como parametro o diretorio que queremos monitorar. Se o diretório nao existir ou não for um diretório uma exceção é lançada. Após, adicionamos a lista de eventos a implementação da Interface FileActionListener, este evento será disparado toda vez que for encontrado um arquivo que esteja de acordo com o regex passado como parametro para DirPoolingController.peekPooling(String regex). (Se você não conhece de regex aqui no forum tem um tutorial só sobre isso). Este mesmo metodo pode ser chamado sem parametros, então, ele traz todos os arquivos do diretório.

O pulo do gato para usar esta classe é usa-la em conjunto com a classe Timer do pacote java.utils, aonde vc pode mandar monitorar a pasta de tempos em tempos, após isso, é só salvar o ultimo array retornado e fazer uma comparação mais profunda (Arrays.deepEquals), ae vc tem na mão os arquivos modificados e que atendam a alguma regra especifica em relação ao nome.

Para usar esta classe em linux vc deve colocar o path para o arquivo de uma forma que ele entenda, né?

public static void main(String...args){
        DirPoolingController dirCtrl = null;
        try {
            dirCtrl = new DirPoolingController("D:\\Minha Pasta");
            dirCtrl.addFileArraived( new FileActionListener() {
                public void peekPerformed(File[] arquivos) {
                    for( int i = 0; i < arquivos.length; i++ ) {
                        System.out.printf( "[%d] - %s\n", i+1, arquivos[i].getName() );
                    }
                }
            } );
            dirCtrl.peekPooling( "(.*?).(txt|abc)" );
        } catch (IOException ex) {
            Logger.getLogger(DirPoolingController.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

Inté!

T

Só lembrando que a classe do Luca não depende de SO, mas você deve usar um intervalo de “polling” razoavelmente alto (maior que 5 segundos, digamos), porque ela lista os arquivos dentro de um diretório, o que não é uma operação muito rápida em Java.

A monitoração “nativa” de diretórios (que, no Windows, é feita através das APIs listadas em Obtaining Directory Change Notifications é mais rápida e eficiente, mas obviamente só funciona em Windows e requer um pouco de programação nativa (C, normalmente).

L

thingol:
Só lembrando que a classe do Luca não depende de SO, mas você deve usar um intervalo de “polling” razoavelmente alto (maior que 5 segundos, digamos), porque ela lista os arquivos dentro de um diretório, o que não é uma operação muito rápida em Java.

A monitoração “nativa” de diretórios (que, no Windows, é feita através das APIs listadas em Obtaining Directory Change Notifications é mais rápida e eficiente, mas obviamente só funciona em Windows e requer um pouco de programação nativa (C, normalmente).

Isto é verdade, no trabalho que participei do desenvolvimento esta foi a solução adotada. Eram gerados em média, 20.000 arquivos ao dia. No java, ele demorava alguns segundos para listar todos, no C era quase instantaneo. A solução adotada foi a de aumentar o tempo de pooling do diretório, mas o intervalo de 1s nos atendeu bem. Claro que vai depender de cada necessidade.

Foi legal você ter comentado isto, pois, sempre que chamar o peekPooling, DESATIVE o timer e volte a ativa-lo só depois do evento peekPerformed ter retornado. Assim pode-se evitar o pooling tão alto.

=)

P.S: Uma coisa importante, a classe limita a quantidade de arquivos retornados, por definição são 300 arquivos! Isto é parametrizado, pode-se escolher um limite qualquer. Não é validado um limite negativo, ele só não retorna nada!

K

Sei que este thread é já antigo mas peguei no código p experimentar e estou a ter o erro

The type new FileActionListener(){} must implement the inherited abstract method FileActionListener.peekPerformed(File[])

quando uso o método addFileArraived.

Não tenho grande experiência a lidar com Listeners em Java confesso.

O que se passa?

L

e alguem sabe como moveria estas informaçoes para um banco de Dados ?

Tipo tenho um novo arquivo na pasta faz um insert para o banco com caminho que vai ser fixo nome hora e tamanho variavel ? para que o monitoramento fosse via JSP?

L

labavel, quais informações? As que estão dentro do arquivo que vc monitorou?

kotoko, vc poderia por gentileza postar parte do seu código?

L

Na realidade estou usando a seguinte função

private BufferedImage buffImage;   
    private PlanarImage image;   
    private String dirInput = "C:/Programas/";   
    private String dirOutput = "C:/Temp/";   
    private String file;   
}

Quero pegar tudo que estiver na pasta"nomes, data e horario, Tamanho" salvar os registros no banco. e depois excluir e mover os Dados

A função acima eu estou utilizando um time que a cada x segundos eu movimento os arquivos, porem com o post publicado posso adptalo para monitorar todos os arquivos por hora de imclusao

L

eu reutilizei seu codigo utilizando um Timer

import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.swing.event.EventListenerList;

/**
 *
 * @author Luis.Leao
 */
public class DirPoolingController {
    public static final Integer DEFAULT_QTD_LIMITE = 300;
    private int limiteQtdPendente = DEFAULT_QTD_LIMITE;
    private int limiteQtdComErro  = DEFAULT_QTD_LIMITE;
    private boolean bRecursive = false;
    /**
     * Lista de eventos.
     */
    private EventListenerList eventList = null;
    private File poolDir = null;

    private class RegexFileFilter implements FileFilter{
        private Pattern padrao = null;
        private Matcher matcher = null;
        private Long qtdRetornados = 0L;
        private int limite;

        public RegexFileFilter(String regex){
            this( regex, DEFAULT_QTD_LIMITE );
        }
       

        public RegexFileFilter(String regex, int limite){
            if( regex != null ) padrao = Pattern.compile(regex);
            this.limite = limite;
        }
        
        public boolean accept(File pathname) {
            if( padrao != null ){
                matcher = padrao.matcher( pathname.getName() );
                qtdRetornados++;
                return qtdRetornados < limite && matcher.find();
            }else return true;
        }
    }

    DirPoolingController(String poolDir) throws IOException{
        this( new File(poolDir) );
    }

    DirPoolingController(File poolDir) throws IOException{
        this( poolDir, false );
    }
    
    DirPoolingController(File poolDir, boolean bRecursive) throws IOException{
        if( !poolDir.isDirectory() ) throw new IOException(String.format("O arquivo %s não é um diretório.", poolDir.getName() ));
        if( !poolDir.exists() ) throw new IOException(String.format("O arquivo %s não existe.", poolDir.getName() ));
        
        this.poolDir = poolDir;
        this.bRecursive = bRecursive;
        eventList = new EventListenerList();
    }

    /**
     * Verifica no diretório os arquivos existentes seguindo o seguinte critério:
     * Retorna os arquivos se e somente se as duas primeiras letras do arquivo
     * existirem no parametro ufSet.
     * @throws IOException Se o diretório não for atingivel ou se não houver nenhum
     * listener para os eventos
     */
    public void peekPooling(String regex) throws IOException{
        RegexFileFilter filtro = null;
        
        if( !poolDir.exists() ){
            throw new IOException( String.format("Diretório inatingivel: %s", poolDir.getPath() ) );
        }else if( eventList.getListenerCount() <= 0 ){
            throw new RuntimeException( "Não existem listeners!" );
        }

        filtro = new RegexFileFilter(regex);

        firePeekPerformed( poolDir.listFiles( filtro ) );
    }
    public void peekPooling() throws IOException{
        peekPooling(null);
    }
    /**
     * Registra a classe para ser notificada quando houver um comando
     * @param l Classe a ser notificada
     */
    public void addFileArraived( FileActionListener l ){
        eventList.add(FileActionListener.class, l);
    }
    /**
     * Desregistra a classe
     * @param l Classe a ser notificada
     */
    public void removeFileArraived( FileActionListener l ){
        eventList.remove(FileActionListener.class, l);
    }

    protected void firePeekPerformed(File arquivos[]){
        FileActionListener listeners[] = null;
        listeners = eventList.getListeners( FileActionListener.class );
        for( FileActionListener listener : listeners ){
            listener.peekPerformed( arquivos );
        }
    }

    public static void main(String...args) throws InterruptedException{
    	for(int i=0;i>=0;i++){
    	Thread.sleep (1000);  
        DirPoolingController dirCtrl = null;
        try {
            dirCtrl = new DirPoolingController("/home/joao/DocsPrint");
            dirCtrl.addFileArraived( new FileActionListener() {
                public void peekPerformed(File[] arquivos) {
                    for( int i = 0; i < arquivos.length; i++ ) {
                       // System.out.printf( "[%d] - %s\n", i+1, arquivos[i].getName() );
                    	 System.out.printf("[%d] - %s\n", i+1, arquivos[i].getName() );
                    }
                }
            } );
            dirCtrl.peekPooling( /*"(.*?).(txt|abc)"*/ );
        } catch (IOException ex) {
            Logger.getLogger(DirPoolingController.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

}
}

Alem do getName como eu pego a hora e o tamanho deste arquivo e gravo so os registros no Banco ?

L

labavel, o código serve simplesmente para retornar os arquivos que se encaixam na expressão regular passada para a classe DirPoolingController, vc deve usar o timer na sua classe e agendar uma tarefa, assim que o timer chamar o metodo run da classe TimerTask vc deve chamar o metodo peekPooling.

Agora, é só pesquisar sobre Timer e TimeTask e usa-los em conjunto com a classe DirPoolingController.

for(int i=0;i>=0;i++)

Cara, esse seu código acima pode ser substituido por while( true ), mas lembre-se que uma condição de parada é importante para o laço, ainda mais se ele estiver rodado na thread principal.

Alguns sites para vc ver como pegar o tamanho do arquivo, a data de criação e o conteúdo.

http://www.exampledepot.com/egs/java.io/getlength.html
http://www.javadb.com/get-file-size
http://java.sun.com/docs/books/tutorial/essential/io/file.html

At

D

Oi Pessoal,

Vou utilizar o codigo que vcs postaram, só estou com 1 dúvida, devo colocar isso em uma thread?

Obrigada

F

Uma opção é essa api da apache.
http://commons.apache.org/jci/

Apesar de ter um propósito diferente, ela tem uma classe

org.apache.commons.jci.monitor.FilesystemAlterationMonitor

Que talvez te ajude.

Essa classe aqui tem um exemplo de uso: https://github.com/fabiofalci/unloading-java-class/blob/instrument/src/instrument/SimpleTransformer.java

L

dheza, vc usar uma thread ou não é opcional. Se vc utilizar o Timer vc usará uma unica Thread e evitará coisas do mal que podem vir ocorrer com multi-thread.

Abraços.

Criado 26 de julho de 2009
Ultima resposta 12 de nov. de 2010
Respostas 15
Participantes 8