代码之家  ›  专栏  ›  技术社区  ›  Nick Fortescue

在爪哇,监视一个文件的最好/最安全的模式是什么?

  •  12
  • Nick Fortescue  · 技术社区  · 17 年前

    其他人的过程是通过在事件发生时一次在csv文件中附加一行来创建该文件。我无法控制文件格式或其他进程,但我知道它只会追加。

    在Java程序中,我想监视这个文件,当附加一行时,读取新行并根据内容作出反应。暂时忽略csv解析问题。监视文件的更改和一次读取一行的最佳方法是什么?

    理想情况下,这将使用标准库类。文件可能在网络驱动器上,所以我想要一些健壮的东西来防止失败。如果可能的话,我宁愿不使用轮询——我宁愿使用某种阻塞解决方案。

    编辑——考虑到标准类不可能有一个阻塞解决方案(感谢您的回答),什么是最健壮的轮询解决方案?我不想每次都重新读取整个文件,因为它可能会变大。

    7 回复  |  直到 11 年前
        1
  •  7
  •   Nick Fortescue    14 年前

    自从Java 7以来 newWatchService() 方法在 FileSystem class .

    但是,有一些警告:

    • 它只是Java 7
    • 这是一种可选方法
    • 它只监视目录,因此您必须自己处理文件,并担心文件移动等。

    在Java 7之前,使用标准API是不可能的。

    我尝试了以下方法(每隔1秒进行一次轮询),但效果良好(只是在处理中打印):

      private static void monitorFile(File file) throws IOException {
        final int POLL_INTERVAL = 1000;
        FileReader reader = new FileReader(file);
        BufferedReader buffered = new BufferedReader(reader);
        try {
          while(true) {
            String line = buffered.readLine();
            if(line == null) {
              // end of file, start polling
              Thread.sleep(POLL_INTERVAL);
            } else {
              System.out.println(line);
            }
          }
        } catch(InterruptedException ex) {
         ex.printStackTrace();
        }
      }
    

    因为没有人提出一个使用当前生产Java的解决方案,我想我会添加它。如果有缺陷,请添加评论。

        2
  •  5
  •   Ramcis    14 年前

    如果使用WatchService类对文件进行更改,则可以注册以获得文件系统的通知。这需要java7,这里是文档的链接 http://docs.oracle.com/javase/tutorial/essential/io/notification.html

    下面是要执行此操作的代码段:

    public FileWatcher(Path dir) {
       this.watcher = FileSystems.getDefault().newWatchService();
       WatchKey key = dir.register(watcher, ENTRY_MODIFY);
    }
    
    void processEvents() {
        for (;;) {
            // wait for key to be signalled
            WatchKey key;
            try {
                key = watcher.take();
            } catch (InterruptedException x) {
                return;
            }
    
            for (WatchEvent<?> event : key.pollEvents()) {
                WatchEvent.Kind<?> kind = event.kind();
    
                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 file \n", event.kind().name(), child);
            }
            // reset key and remove from set if directory no longer accessible
            boolean valid = key.reset();
        }
    }
    
        3
  •  3
  •   Stephen Denne    17 年前

    使用Java 7的 WatchService NiO 2的一部分

    WatchService API专为需要通知有关文件更改事件的应用程序而设计。

        4
  •  2
  •   Community Mohan Dere    9 年前

    对于标准库类,这是不可能的。看到这个 question 详情。

    为了有效地进行投票,最好使用 Random Access . 如果您记住文件最后一个结尾的位置并从那里开始读取,这将有所帮助。

        5
  •  2
  •   Jeff Atwood    16 年前

    下面是两个可以并发运行的类(例如,在两个不同的shell窗口中),这表明一个给定的文件可以同时被一个进程写入并被另一个进程读取。

    这里,这两个进程将执行这些Java类,但我假定编写过程可以来自任何其他应用程序。(假设它对文件没有独占锁,那么在某些操作系统上是否存在这样的文件系统锁?)

    我已经在windoze和linux上成功地测试了这两个类。我非常想知道他们是否在某些情况下(例如操作系统)失败。

    第1类:

    import java.io.File;
    import java.io.FileWriter;
    import java.io.PrintWriter;
    
    public class FileAppender {
    
        public static void main(String[] args) throws Exception {
            if ((args != null) && (args.length != 0)) throw
                new IllegalArgumentException("args is not null and is not empty");
    
            File file = new File("./file.txt");
            int numLines = 1000;
            writeLines(file, numLines);
        }
    
        private static void writeLines(File file, int numLines) throws Exception {
            PrintWriter pw = null;
            try {
                pw = new PrintWriter( new FileWriter(file), true );
                for (int i = 0; i < numLines; i++) {
                    System.out.println("writing line number " + i);
                    pw.println("line number " + i);
                    Thread.sleep(100);
                }
            }
            finally {
                if (pw != null) pw.close();
            }
        }
    
    }
    

    第2类:

    import java.io.BufferedReader;
    import java.io.File;
    import java.io.FileReader;
    
    public class FileMonitor {
    
        public static void main(String[] args) throws Exception {
            if ((args != null) && (args.length != 0)) throw
                new IllegalArgumentException("args is not null and is not empty");
    
            File file = new File("./file.txt");
            readLines(file);
        }
    
        private static void readLines(File file) throws Exception {
            BufferedReader br = null;
            try {
                br = new BufferedReader( new FileReader(file) );
                while (true) {
                    String line = br.readLine();
                    if (line == null) { // end of file, start polling
                        System.out.println("no file data available; sleeping..");
                        Thread.sleep(2 * 1000);
                    }
                    else {
                        System.out.println(line);
                    }
                }
            }
            finally {
                if (br != null) br.close();
            }
        }
    
    }
    
        6
  •  1
  •   marko    15 年前

    不幸的是,TestInPixStand类可以用来监视文件的结束,但它不是标准的Java平台类,但是在Web上很少有实现。您可以找到tailinputstream类的实现以及上的用法示例 http://www.greentelligent.com/java/tailinputstream .

        7
  •  0
  •   chornbe    11 年前

    在一致周期或随机周期上进行轮询;200-2000毫秒应是一个良好的随机轮询间隔范围。

    检查两件事…

    如果必须监视文件增长,请检查eof/字节计数,并确保将其与lass轮询中的fileaccess或filewrite时间进行比较。如果(>),则文件已被写入。

    然后,将其与检查独占锁定/读取访问结合起来。如果文件可以被读取锁定并且已经增长,那么对它所写的一切都已经完成。

    单独检查任何一个属性都不一定能保证您的书面状态++ 实际完成并可供使用。