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

StackOverflow https://stackoverflow.com/questions/1073274

  •  21-08-2019
  •  | 
  •  

Вопрос

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

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

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

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

Это было полезно?

Решение

Начиная с Java 7, существует newWatchService() метод на основе Класс файловой системы.

Однако есть некоторые оговорки:

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

Другие советы

Вы можете зарегистрироваться, чтобы получать уведомления от файловой системы, если в файле происходят какие-либо изменения, используя класс 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 Часовой сервис, часть NIO.2

API WatchService предназначен для приложений, которые должны получать уведомления о событиях изменения файла.

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

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

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

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

}

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

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

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

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top