Вопрос по java, file – В Java, какой самый лучший / самый безопасный шаблон для отслеживания добавляемого файла?

12

Чужой процесс создает файл CSV, добавляя к нему строку по мере возникновения событий. У меня нет контроля над форматом файла или другим процессом, но я знаю, что он будет только добавляться.

В Java-программе я хотел бы отслеживать этот файл, и когда строка добавляется, читайте новую строку и реагируйте в соответствии с содержимым. Пока игнорируйте проблему с анализом CSV. Каков наилучший способ отслеживать файл на предмет изменений и читать строки одновременно?

В идеале для этого будут использоваться стандартные библиотечные классы. Файл может находиться на сетевом диске, поэтому я хотел бы, чтобы что-то было устойчивым к сбоям. Я бы предпочел не использовать опрос, если это возможно, - вместо этого я бы предпочел какое-либо блокирующее решение.

Изменить - учитывая, что блокирующее решение невозможно со стандартными классами (спасибо за этот ответ), какое решение для опроса является наиболее надежным? Я бы предпочел не перечитывать весь файл каждый раз, так как он может стать довольно большим.

Ваш Ответ

7   ответов
1

К сожалению, класс TailInputStream, который можно использовать для контроля конца файла, не является одним из стандартных классов платформы Java, но в Интернете существует несколько реализаций. Вы можете найти реализацию класса TailInputStream вместе с примером использования наhttp://www.greentelligent.com/java/tailinputstream.

5

Вы можете зарегистрироваться, чтобы получать уведомления от файловой системы, если какие-либо изменения происходят с файлом, используя класс 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();
    }
}
Не могли бы вы отредактировать свой ответ, чтобы сказать, что: это новое в Java 7, оно в java.nio, и что newWatchService () является необязательным методом. Может быть, добавить ссылку на Javadoc? Nick Fortescue
2

Просто для расширения последней записи Ника Фортескью ниже приведены два класса, которые вы можете запускать одновременно (например, в двух разных окнах оболочки), которые показывают, что данный файл может быть одновременно записан одним процессом и прочитан другим.

Здесь два процесса будут выполнять эти классы 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();
        }
    }

}
Запуск этих двух по отдельности работает для меня, но если я запускаю только FileMonitor и вручную редактирую file.txt с помощью vim, изменения не распознаются. Мысли?
0

Опрос либо по последовательному циклу, либо по случайному циклу; 200-2000 мс должны быть хорошим интервалом случайного опроса.

Проверьте две вещи ...

Если вам нужно следить за ростом файла, то проверьте количество EOF / байтов и обязательно сравните это время и время fileAccess или fileWrite с опросом lass. Если (& gt;), то файл записан.

Затем совместите это с проверкой монопольного доступа на блокировку / чтение. Если файл можно заблокировать для чтения и он вырос, то все, что записывалось в него, завершено.

Проверка одного свойства не обязательно даст вам гарантированное состояние написанного ++and фактически сделано и доступно для использования.

3

Использовать Java 7WatchService, часть NIO.2

The WatchService API is designed for applications that need to be notified about file change events.

В настоящее время существует либо ранняя версия предварительного просмотра доступа, либо последняя версия двоичного снимка.
Ух ты, Java 7 выпущена? Должно быть, я ухожу в пещеру довольно долго.
WatchService следит за каталогами, а не за файлами
2

Это невозможно при использовании стандартных библиотечных классов. Видеть этовопрос для деталей.

Для эффективного опроса лучше использоватьПроизвольный доступ, Это поможет, если вы запомните позицию последнего конца файла и начнете читать оттуда.

Спасибо. Поскольку я отредактировал вопрос, чтобы отразить его, это означает, что мне нужно решение для опроса. Есть ли у вас какие-либо предложения о том, что является наиболее надежным / эффективным? Nick Fortescue
7

Начиная с Java 7 былоnewWatchService () метод наКласс FileSystem.

Однако есть несколько предостережений:

  • It is only Java 7
  • It is an optional method
  • it only watches directories, so you have to do the file handling yourself, and worry about the file moving etc

До 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, я подумал, что добавлю его. Если есть недостатки, пожалуйста, добавьте в комментариях.

Вышеуказанные коды гарантируют чтение добавленной строки?
Мое требование состоит в том, чтобы просмотреть папку и, как только файл будет добавлен / записан / перемещен в папку, немедленно предпримите необходимые действия (например, отправьте файл по электронной почте). Проблема, с которой я сталкиваюсь, заключается в том, что когда файл большой, для завершения записи или копирования может потребоваться некоторое время, в то время как событие FILE_CREATE объявляется, как только первые байты файла записываются в папку. Поэтому я не могу выполнить действие немедленно. Какой надежный способ определить, был ли файл полностью записан перед выполнением каких-либо действий с ним?

Похожие вопросы