В Java, что является лучшим/безопасным шаблоном для мониторинга присоединенного файла?
Кто-то другой процесс создает CSV файл, добавляя к нему строку за раз, когда происходят события. Я не контролирую формат файла или другой процесс, но я знаю, что он будет только добавляться.
В программе Java я хотел бы следить за этим файлом, а при добавлении строки читать новую строку и реагировать в соответствии с содержимым. Игнорируйте проблему синтаксического анализа CSV. Каков наилучший способ контролировать файл для изменений и читать строку за раз?
В идеале это будет использовать стандартные классы библиотек. Возможно, файл находится на сетевом диске, поэтому мне нужно что-то надежное для отказа. Я бы предпочел не использовать опрос, если это возможно - я предпочел бы какое-то блокирующее решение.
Изменить - если для стандартных классов (спасибо за этот ответ) блокирующее решение невозможно, то что является самым надежным опросным решением? Я бы предпочел не перечитывать весь файл каждый раз, так как он мог расти довольно большим.
Ответы
Ответ 1
Так как Java 7 был метод newWatchService() на Класс FileSystem.
Однако есть некоторые оговорки:
- Это только 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
Вы можете зарегистрироваться, чтобы получать уведомления в файловой системе, если какие-либо изменения происходят с файлом с использованием класса 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
Использовать Java 7 WatchService, часть NIO.2
API WatchService предназначен для приложений, которые должны быть уведомлены о событиях смены файлов.
Ответ 4
Это невозможно для стандартных классов библиотеки. Подробнее см. question.
Для эффективного опроса лучше использовать Random Access. Это поможет, если вы запомните позицию последнего конца файла и начнете читать оттуда.
Ответ 5
Чтобы развернуть последнюю запись Nick Fortescue, ниже приведены два класса, которые вы можете запускать одновременно (например, в двух разных окнах оболочки), которые показывают, что данный файл может быть одновременно написан одним процессом и считан другим.
Здесь два процесса будут выполнять эти классы 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
К сожалению, класс TailInputStream, который может использоваться для мониторинга конца файла, не является стандартным классом Java-платформы Java, но в Интернете мало реализаций. Вы можете найти реализацию класса TailInputStream вместе с примером использования http://www.greentelligent.com/java/tailinputstream.
Ответ 7
Вероятно, это не поможет, просто подумайте громко, но в UNIX можно было бы использовать
хвост -f
чтобы увидеть любые строки, добавленные в файл - вам нужно что-то подобное, но с точки зрения классов java.
tail -f сам реализуется с опросом, я считаю. Он сталкивается с EOF, затем ждет небольшое количество времени (100 мс), затем пытается снова прочитать EOF. Таким образом, он всегда получает последние данные, потому что в то время как другие записи процесса - EOF перемещается вперед.
Ответ 8
Опрос, будь то последовательный цикл или случайный цикл; 200-2000 мс должен быть хорошим интервалом интервала опроса.
Проверьте две вещи...
Если вам нужно следить за ростом файла, проверьте количество EOF/байтов и не забудьте сравнить это, а также время fileAccess или fileWrite с опросом lass. Если ( > ), то файл был записан.
Затем объедините это с проверкой на эксклюзивный доступ к блокировке/чтению. Если файл может быть заблокирован для чтения и он вырос, то все, что было написано, завершено.
Проверка на наличие одного из свойств не обязательно обеспечит вам гарантированное состояние написанного ++ и фактически сделана и доступна для использования.