programing

Java에서 파일이 변경된 수신기

nicescript 2022. 10. 20. 21:24
반응형

Java에서 파일이 변경된 수신기

파일 시스템에서 파일이 변경되면 알려 주셨으면 합니다.last Modified File 속성을 폴링하는 스레드만 찾았습니다.이 솔루션은 최적이 아닙니다.

이전에 로그 파일 모니터를 작성한 적이 있는데, 1초에 여러 번 단일 파일의 속성을 폴링하는 것이 시스템 성능에 미치는 영향은 실제로 매우 작다는 것을 알게 되었습니다.

Java 7은 NIO.2의 일부로 WatchService API를 추가했습니다.

WatchService API는 파일 변경 이벤트에 대해 알림을 받아야 하는 응용 프로그램을 위해 설계되었습니다.

Apache Commons의 VFS API를 사용하고 있습니다.다음 예에서는 퍼포먼스에 큰 영향을 주지 않고 파일을 감시하는 방법을 보여 줍니다.

기본 파일 모니터

jnotify라는 lib가 있으며 inotify를 Linux로 랩하고 Windows를 지원합니다.써본 적도 없고 얼마나 좋은지는 모르겠지만 시도해 볼 만해요.

JDK 1.7 이후 파일 변경에 대해 응용 프로그램에 통지하는 표준 방법은 WatchService API를 사용하는 것입니다.Watch Service는 이벤트 중심입니다.공식 튜토리얼에서는 다음 예를 제시합니다.

/*
 * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *   - Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *
 *   - Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 *
 *   - Neither the name of Oracle nor the names of its
 *     contributors may be used to endorse or promote products derived
 *     from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

import java.nio.file.*;
import static java.nio.file.StandardWatchEventKinds.*;
import static java.nio.file.LinkOption.*;
import java.nio.file.attribute.*;
import java.io.*;
import java.util.*;

/**
 * Example to watch a directory (or tree) for changes to files.
 */

public class WatchDir {

    private final WatchService watcher;
    private final Map<WatchKey,Path> keys;
    private final boolean recursive;
    private boolean trace = false;

    @SuppressWarnings("unchecked")
    static <T> WatchEvent<T> cast(WatchEvent<?> event) {
        return (WatchEvent<T>)event;
    }

    /**
     * Register the given directory with the WatchService
     */
    private void register(Path dir) throws IOException {
        WatchKey key = dir.register(watcher, ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY);
        if (trace) {
            Path prev = keys.get(key);
            if (prev == null) {
                System.out.format("register: %s\n", dir);
            } else {
                if (!dir.equals(prev)) {
                    System.out.format("update: %s -> %s\n", prev, dir);
                }
            }
        }
        keys.put(key, dir);
    }

    /**
     * Register the given directory, and all its sub-directories, with the
     * WatchService.
     */
    private void registerAll(final Path start) throws IOException {
        // register directory and sub-directories
        Files.walkFileTree(start, new SimpleFileVisitor<Path>() {
            @Override
            public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs)
                throws IOException
            {
                register(dir);
                return FileVisitResult.CONTINUE;
            }
        });
    }

    /**
     * Creates a WatchService and registers the given directory
     */
    WatchDir(Path dir, boolean recursive) throws IOException {
        this.watcher = FileSystems.getDefault().newWatchService();
        this.keys = new HashMap<WatchKey,Path>();
        this.recursive = recursive;

        if (recursive) {
            System.out.format("Scanning %s ...\n", dir);
            registerAll(dir);
            System.out.println("Done.");
        } else {
            register(dir);
        }

        // enable trace after initial registration
        this.trace = true;
    }

    /**
     * Process all events for keys queued to the watcher
     */
    void processEvents() {
        for (;;) {

            // wait for key to be signalled
            WatchKey key;
            try {
                key = watcher.take();
            } catch (InterruptedException x) {
                return;
            }

            Path dir = keys.get(key);
            if (dir == null) {
                System.err.println("WatchKey not recognized!!");
                continue;
            }

            for (WatchEvent<?> event: key.pollEvents()) {
                WatchEvent.Kind kind = event.kind();

                // TBD - provide example of how OVERFLOW event is handled
                if (kind == OVERFLOW) {
                    continue;
                }

                // Context for directory entry event is the file name of entry
                WatchEvent<Path> ev = cast(event);
                Path name = ev.context();
                Path child = dir.resolve(name);

                // print out event
                System.out.format("%s: %s\n", event.kind().name(), child);

                // if directory is created, and watching recursively, then
                // register it and its sub-directories
                if (recursive && (kind == ENTRY_CREATE)) {
                    try {
                        if (Files.isDirectory(child, NOFOLLOW_LINKS)) {
                            registerAll(child);
                        }
                    } catch (IOException x) {
                        // ignore to keep sample readbale
                    }
                }
            }

            // reset key and remove from set if directory no longer accessible
            boolean valid = key.reset();
            if (!valid) {
                keys.remove(key);

                // all directories are inaccessible
                if (keys.isEmpty()) {
                    break;
                }
            }
        }
    }

    static void usage() {
        System.err.println("usage: java WatchDir [-r] dir");
        System.exit(-1);
    }

    public static void main(String[] args) throws IOException {
        // parse arguments
        if (args.length == 0 || args.length > 2)
            usage();
        boolean recursive = false;
        int dirArg = 0;
        if (args[0].equals("-r")) {
            if (args.length < 2)
                usage();
            recursive = true;
            dirArg++;
        }

        // register directory and process its events
        Path dir = Paths.get(args[dirArg]);
        new WatchDir(dir, recursive).processEvents();
    }
}

개별 파일에는 다음과 같은 다양한 솔루션이 있습니다.

Apache VFS는 폴링 알고리즘을 사용하지만 기능이 향상될 수 있습니다.또한 API는 파일이 닫혔는지 여부를 확인하는 방법을 제공하지 않습니다.

Java commons-io에는 FileAlterationObserver가 있습니다.FileAlterationMonitor와 조합하여 폴링을 수행합니다.일반 VFS와 유사합니다.장점은 의존성이 훨씬 적다는 것이다.

edit: 의존관계가 적다는 것은 사실이 아닙니다.VFS의 경우 의존관계는 옵션입니다.단, VFS 추상화 레이어 대신 Java 파일을 사용합니다.

이 코드 조각은 속성 파일을 읽을 때마다 실행되며, 마지막으로 파일을 읽은 이후 변경된 경우에만 실제로 파일을 읽습니다.이게 도움이 됐으면 좋겠네요.

private long timeStamp;
private File file;

private boolean isFileUpdated( File file ) {
  this.file = file;
  this.timeStamp = file.lastModified();

  if( this.timeStamp != timeStamp ) {
    this.timeStamp = timeStamp;
    //Yes, file is updated
    return true;
  }
  //No, file is not updated
  return false;
}

Log4J에서도 같은 어프로치가 사용되고 있습니다.

「더 많은 NIO 기능」에는, 파일 워치 기능이 있어, 실장은 기반이 되는 OS에 의존합니다.JDK7에 있을 거예요

업데이트: Java SE 7에 추가되었습니다.Chris Janicki는 관련 Java 튜토리얼 링크를 제공합니다.

FileReader를 사용하여 파일 변경을 청취할 수 있습니다.아래의 예를 참조해 주세요.

// File content change listener 
private String fname;
private Object lck = new Object();
... 
public void run()
{
    try
    {
        BufferedReader br = new BufferedReader( new FileReader( fname ) );
        String s;
        StringBuilder buf = new StringBuilder();
        while( true )
        {
            s = br.readLine();
            if( s == null )
            {
                synchronized( lck )
                {
                    lck.wait( 500 );
                }
            }
            else
            {
               System.out.println( "s = " + s );
            }

        }
    }
    catch( Exception e )
    {
        e.printStackTrace();
    }
}

만약 당신이 약간의 돈을 지불하고 싶다면, JNIWrapper는 Winpack과 함께 유용한 라이브러리입니다. 당신은 특정 파일에서 파일 시스템 이벤트를 얻을 수 있습니다.안타깝게도 창밖에는 없습니다.

https://www.teamdev.com/jniwrapper 를 참조해 주세요.

그렇지 않으면 네이티브코드에 의존하는 것이 항상 나쁜 것은 아닙니다.특히 네이티브이벤트에 대한 폴링 메커니즘이 베스트인 경우에는 더욱 그렇습니다.

일부 컴퓨터에서는 Java 파일 시스템 작업이 느릴 수 있으며 제대로 처리되지 않으면 애플리케이션 성능에 영향을 미치기 쉽습니다.

Apache Commons JCI(Java 컴파일러 인터페이스)도 고려할 수 있습니다.이 API는 동적 클래스 컴파일에 초점을 맞춘 것으로 보이지만 파일 변경을 감시하는 클래스도 API에 포함합니다.

예: http://commons.apache.org/jci/usage.html

Spring Integration은 디렉토리 및 파일을 보기 위한 훌륭한 메커니즘을 제공합니다.http://static.springsource.org/spring-integration/reference/htmlsingle/ # files크로스 플랫폼(Mac, Linux 및 Windows에서 사용한 적이 있음)인 것은 확실합니다.

JxFileWatcher라고 불리는 파일 및 폴더를 감시하는 상용 크로스 데스크톱 라이브러리가 있습니다.http://www.teamdev.com/jxfilewatcher/ 에서 다운로드 할 수 있습니다.

또, http://www.teamdev.com/jxfilewatcher/onlinedemo/ 에서 동작하고 있는 것을 확인할 수 있습니다.

다른 답변과 마찬가지로 File, Timer 및 TimerTask를 사용하여 설정한 간격으로 백그라운드스레드 폴링으로 실행할 수 있도록 하는 방법을 나타냅니다.

import java.io.File;
import java.util.Timer;
import java.util.TimerTask;

public class FileModifiedWatcher
{
  private static File file;
  private static int pollingInterval;
  private static Timer fileWatcher;
  private static long lastReadTimeStamp = 0L;

  public static boolean init(String _file, int _pollingInterval)
  {
    file =  new File(_file);
    pollingInterval = _pollingInterval; // In seconds

    watchFile();

    return true;
  }

  private static void watchFile()
  {
    if ( null == fileWatcher )
    {
      System.out.println("START");

      fileWatcher = new Timer();

      fileWatcher.scheduleAtFixedRate(new TimerTask()
      {
        @Override
        public void run()
        {

          if ( file.lastModified() > lastReadTimeStamp )
          {
            System.out.println("File Modified");
          }

          lastReadTimeStamp = System.currentTimeMillis();
        }
      }, 0, 1000 * pollingInterval);
    }

  }
}

마지막으로 수정된 파일 속성을 폴링하는 것은 간단하지만 효과적인 솔루션입니다.클래스만 정의하면 됩니다.FileChangedWatcher를 실장합니다.onModified()방법:

import java.io.File;

public abstract class FileChangedWatcher
{
    private File file;

    public FileChangedWatcher(String filePath)
    {
        file = new File(filePath);
    }

    public void watch() throws InterruptedException
    {
        long currentModifiedDate = file.lastModified();

        while (true)
        {
            long newModifiedDate = file.lastModified();

            if (newModifiedDate != currentModifiedDate)
            {
                currentModifiedDate = newModifiedDate;
                onModified();
            }

            Thread.sleep(100);
        }
    }

    public String getFilePath()
    {
        return file.getAbsolutePath();
    }

    protected abstract void onModified();
}

언급URL : https://stackoverflow.com/questions/494869/file-changed-listener-in-java

반응형