在 Java 中,监视附加文件的最佳/最安全模式是什么?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1073274/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
In Java, what is the best/safest pattern for monitoring a file being appended to?
提问by Nick Fortescue
Someone else's process is creating a CSV file by appending a line at a time to it, as events occur. I have no control over the file format or the other process, but I know it will only append.
其他人的进程正在通过在事件发生时一次添加一行来创建 CSV 文件。我无法控制文件格式或其他进程,但我知道它只会追加。
In a Java program, I would like to monitor this file, and when a line is appended read the new line and react according to the contents. Ignore the CSV parsing issue for now. What is the best way to monitor the file for changes and read a line at a time?
在 Java 程序中,我想监视这个文件,当追加一行时,读取新行并根据内容做出反应。暂时忽略 CSV 解析问题。监视文件更改并一次读取一行的最佳方法是什么?
Ideally this will use the standard library classes. The file may well be on a network drive, so I'd like something robust to failure. I'd rather not use polling if possible - I'd prefer some sort of blocking solution instead.
理想情况下,这将使用标准库类。该文件很可能位于网络驱动器上,所以我想要一些可以防止失败的东西。如果可能,我宁愿不使用轮询 - 我更喜欢某种阻塞解决方案。
Edit -- given that a blocking solution is not possible with standard classes (thanks for that answer), what is the most robust polling solution? I'd rather not re-read the whole file each time as it could grow quite large.
编辑 - 鉴于标准类无法使用阻塞解决方案(感谢您的回答),最强大的轮询解决方案是什么?我不想每次都重新读取整个文件,因为它可能会变得非常大。
采纳答案by Nick Fortescue
Since Java 7 there has been the newWatchService()method on the FileSystem class.
从 Java 7 开始,FileSystem 类就有了newWatchService()方法。
However, there are some caveats:
但是,有一些注意事项:
- 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
- 这是一个可选的方法
- 它只监视目录,因此您必须自己处理文件,并担心文件移动等
Before Java 7 it is not possible with standard APIs.
在 Java 7 之前,标准 API 是不可能的。
I tried the following (polling on a 1 sec interval) and it works (just prints in processing):
我尝试了以下(以 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();
}
}
As no-one else has suggested a solution which uses a current production Java I thought I'd add it. If there are flaws please add in comments.
由于没有其他人建议使用当前生产的 Java 的解决方案,我想我会添加它。如有不足之处请评论补充。
回答by Ramcis
You can register to get notified by the file system if any change happens to the file using WatchService class. This requires Java7, here the link for the documentation http://docs.oracle.com/javase/tutorial/essential/io/notification.html
如果使用 WatchService 类对文件发生任何更改,您可以注册以获取文件系统的通知。这需要 Java7,这里是文档的链接 http://docs.oracle.com/javase/tutorial/essential/io/notification.html
here the snippet code to do that:
这里的代码片段可以做到这一点:
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();
}
}
回答by kgiannakakis
This is not possible with standard library classes. See this questionfor details.
这对于标准库类是不可能的。有关详细信息,请参阅此问题。
For efficient polling it will be better to use Random Access. It will help if you remember the position of the last end of file and start reading from there.
为了高效轮询,最好使用Random Access。如果您记住文件最后一个结尾的位置并从那里开始阅读,这将有所帮助。
回答by Stephen Denne
Use Java 7's WatchService, part of NIO.2
使用 Java 7 的WatchService,NIO.2 的一部分
The WatchService API is designed for applications that need to be notified about file change events.
WatchService API 设计用于需要通知文件更改事件的应用程序。
回答by Brent Boyer
Just to expand on Nick Fortescue's last entry, below are two classes that you can run concurrently (e.g. in two different shell windows) which shows that a given File can simultaneously be written to by one process and read by another.
只是为了扩展 Nick Fortescue 的最后一个条目,下面是您可以同时运行的两个类(例如,在两个不同的 shell 窗口中),这表明给定的文件可以同时由一个进程写入并由另一个进程读取。
Here, the two processes will be executing these Java classes, but I presume that the writing process could be from any other application. (Assuming that it does not hold an exclusive lock on the file-are there such file system locks on certain operating systems?)
在这里,两个进程将执行这些 Java 类,但我假设写入进程可能来自任何其他应用程序。(假设它没有对文件持有独占锁——在某些操作系统上有这样的文件系统锁吗?)
I have successfully tested these two classes on both Windoze and Linux. I would very much like to know if there is some condition (e.g. operating system) on which they fail.
我已经在 Windoze 和 Linux 上成功测试了这两个类。我非常想知道是否存在某些条件(例如操作系统)导致它们失败。
Class #1:
第 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();
}
}
}
Class #2:
第 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();
}
}
}
回答by marko
Unfortunately, TailInputStream class, which can be used to monitor the end of a file, is not one of standard Java platform classes, but there are few implementations on the web. You can find an implementation of TailInputStream class together with a usage example on http://www.greentelligent.com/java/tailinputstream.
不幸的是,可用于监视文件结尾的 TailInputStream 类不是标准的 Java 平台类之一,但网络上的实现很少。您可以在http://www.greentelligent.com/java/tailinputstream上找到 TailInputStream 类的实现以及使用示例。
回答by ChrisH
Poll, either on a consistent cycle or on a random cycle; 200-2000ms should be a good random poll interval span.
轮询,无论是在一致周期还是随机周期;200-2000ms 应该是一个很好的随机轮询间隔跨度。
Check two things...
检查两件事...
If you have to watch for file growth, then check the EOF / byte count, and be sure to compare that and the fileAccess or fileWrite times with the lass poll. If ( > ), then the file has been written.
如果您必须注意文件增长,请检查 EOF / 字节数,并确保将其与 fileAccess 或 fileWrite 时间与 lass poll 进行比较。如果 ( > ),则文件已写入。
Then, combine that with checking for exclusive lock / read access. If the file can be read-locked and it has grown, then whatever was writing to it has finished.
然后,将其与检查排他锁/读取访问结合起来。如果文件可以被读锁定并且它已经增长,那么写入它的任何内容都已经完成。
Checking for either property alone won't necessarily get you a guaranteed state of written++ andactually done and available for use.
单独检查任一属性并不一定能让您保证书面 ++和实际完成并可供使用。

