在java watchservice中实现重命名和删除 [英] Implementing renaming and deletion in java watchservice
问题描述
我的假设是:当文件被重命名时,执行三个操作
- 删除文件xxx
- 创建文件yyy
- 修改文件yyy
以下是我的代码:
MyWatcher.java
import java.io.IOException;
import java.nio.file.FileSystems;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardWatchEventKinds;
import java.nio.file.WatchEvent;
import java.nio.file.WatchKey;
import java.nio.file.WatchService;
import java.util.ArrayList;
import java.util.List;
public class MyWatcher {
@SuppressWarnings(rawtypes)
public static void main(String [] strings){
路径myWatchPath = Paths.get(D:\\log4j);
long preventDuplicateTime = 0;
FileDelete onDelete = new FileDelete(); //此对象必须是线程安全
列表< String> notifications = new ArrayList< String>();
WatchService myPathWatchService = null;
尝试{
myPathWatchService = FileSystems.getDefault()。newWatchService();
} catch(IOException e){
e.printStackTrace();
}
try {
myWatchPath.register(myPathWatchService,
StandardWatchEventKinds.ENTRY_CREATE,
StandardWatchEventKinds.ENTRY_DELETE,
StandardWatchEventKinds.ENTRY_MODIFY);
} catch(IOException e){
e.printStackTrace();
}
boolean isKeyValid = true;
while(isKeyValid){
WatchKey myPathWatchKey = null;
try {
myPathWatchKey = myPathWatchService.take();
} catch(InterruptedException e){
e.printStackTrace(); // throw
}
for(WatchEvent watchEvent:myPathWatchKey.pollEvents()){
/ /WatchEvent.Kind kind = watchEvent.kind();
if(StandardWatchEventKinds.ENTRY_CREATE.equals(watchEvent
.kind())){
String fileName = watchEvent.context()。toString();
if(onDelete.status == -1)
System.out.println(File Created:+ fileName +
+ watchEvent.context());
else {
if(onDelete.status == 0){
onDelete.createdTime = System.nanoTime();
if(onDelete.deletedTime / 10000000 == onDelete.createdTime / 10000000){
onDelete.createdFile = watchEvent.context()。toString();
onDelete.status ++;
notifications.add(File Created:+ fileName);
} else {
for(String string:notifications){
System.out.println(string);
}
notifications = new ArrayList< String>();
System.out.println(File Created:+ fileName +
+ watchEvent.context());
onDelete = new FileDelete(); //持续时间不近(似乎没有重命名)
}
} else {
//这不应该来这里!
onDelete = new FileDelete();
}
}
}
if(StandardWatchEventKinds.ENTRY_DELETE.equals(watchEvent
.kind())){
String fileName = watchEvent.context )的ToString();
if(onDelete.status == -1){
onDelete = new FileDelete();
onDelete.status ++;
onDelete.deletedFile = watchEvent.context()。toString();
onDelete.deletedTime = System.nanoTime();
notifications.add(File deleted:+ fileName);
}
//System.out.println(\"File deleted:+ fileName); // push to notfication to array for later use
}
if(StandardWatchEventKinds.ENTRY_MODIFY.equals(watchEvent
.kind())){
long current = System.nanoTime );
String fileName = watchEvent.context()。toString();
if(!(preventDuplicateTime / 10000000 == current / 10000000))
notifications.add(文件修改:+ fileName);
preventDuplicateTime =(System.nanoTime());
onDelete.modifiedFile = fileName;
onDelete.modifiedTime = System.nanoTime();
if(onDelete.status!= 1){
for(String messages:notifications){
System.out.println(messages);
}
onDelete = new FileDelete();
notifications = new ArrayList< String>();
}
else if(onDelete.createdFile.equals(onDelete.modifiedFile))
if(onDelete.createdTime / 10000000 == onDelete.modifiedTime / 10000000){
System.out .println(文件重命名为+ fileName);
onDelete = new FileDelete();
notifications = new ArrayList< String>();
}
}
/ *} * /
}
isKeyValid = myPathWatchKey.reset();
}
}
}
FileRename.java
public class FileRename {
int status = -1;
String deletedFile =;
long deletedTime = 0;
String createdFile =;
long createdTime = 0;
String modifiedFile =;
long modifiedTime = 0;
}
它在重命名操作上显示完美,但问题是我无法知道如何为onDelete显示。因为每个删除都被推入通知!或者帮助我实现重命名!
*注意请不要建议第三方的罐子! (因为大多数人,像JNotify,都是依赖于操作系统)
请在下面找到解释为什么独立于操作系统的解决方案不会工作。为什么Java WatchService
的事件粒度对于您想要实现的程度来说太弱了。
基于可用的事件(CREATE,MODIFY,DELETE),您无法确定发生了哪个操作。
在Linux机器上执行以下示例
创建一些测试文件
touch / tmp / stackoverflow / foo / tmp / stackoverflow / foo2
执行以下命令
rm foo&& cp foo2吧&&回声foo> bar
这将创建以下事件(使用 WatchDir.java )
ENTRY_DELETE ..:..:.. [.........]:/ tmp / stackoverflow / foo
ENTRY_CREATE 20:09:37 [rw.rw.rw.]:/ tmp / stackoverflow / bar
ENTRY_MODIFY 20:09:37 [rw.rw.rw.]:/ tmp / stackoverflow / bar
在您假定事件顺序之后,这将是一个重命名
操作。
Wheras mv bar foobar
创建以下事件。 p>
ENTRY_DELETE ..:..:.. [.........]:/ tmp / stackoverflow / bar
ENTRY_CREATE 19:55:37 [rw.rw.rw.]:/ tmp / stackoverflow / foobar
现在对于Window机器来说相同
创建一些测试文件
rem。> c:/ temp / stackoverflow / foo
rem。> c:/ tmp / stackoverflow / foo2
执行以下命令
del foo
copy foo2 bar
这将创建以下事件
ENTRY_DELETE ..:..:.. [.........]:c:\temp\stackoverflow\foo
ENTRY_CREATE 19:59:10 [.........]:c:\temp\stackoverflow\bar
ENTRY_MODIFY 19:59:10 [......... ]:c:\temp\stackoverflow\bar
在您假定事件后,将会是重命名
动作。
Wheras ren bar foobar
在这种情况下创建相同的事件顺序。
ENTRY_DELETE ..:..:.. [..... ....]:c:\temp\stackoverflow\bar
ENTRY_CREATE 20:02:41 [.........]:c:\temp\stackoverflow\foobar
ENTRY_MODIFY 20:02:41 [......... ]:c:\temp\stackoverflow\foobar
与 iwatch / tmp / stackoverflow /
在Linux机器上,您可以确切地确定发生了什么。
执行命令 rm foo &安培;&安培; cp foo2吧&&回声foo> bar
产生以下inotify事件。
[31 /Mär/ 2015 20:25 :40] IN_DELETE / tmp / stackoverflow // foo
[31 /Mär/ 2015 20:25:40] * / tmp / stackoverflow // foo is deleted
[31 /Mär/ 2015 20:25 :40] IN_CREATE / tmp / stackoverflow // bar
[31 /Mär/ 2015 20:25:40] IN_CLOSE_WRITE / tmp / stackoverflow // bar
[31 /Mär/ 2015 20:25:40 ] * / tmp / stackoverflow // bar被关闭
[31 /Mär/ 2015 20:25:40] IN_CLOSE_WRITE / tmp / stackoverflow // bar
[31 /Mär/ 2015 20:25:40 ] * / tmp / stackoverflow // bar被关闭
而 mv bar foobar
创建以下inotify事件
[31 /Mär/ 2015 20:27:10] IN_MOVED_FROM / tmp / stackoverflow // bar
[31 /Mär/ 2015 20:27:10] IN_MOVED_TO / tmp / stackoverflow // foobar
[31 /Mär/ 2015 20:27:10] * / tmp / stackoverflow // bar被移动到/ tmp / stackoverflow // foobar
编辑相关的Java源代码来确认我的解释。没有办法找到一个文件是否被重命名,一个文件被删除,另一个文件被创建(同时)。
< a href =http://hg.openjdk.java.net/jdk7/jdk7/jdk/file/9b8c96f96a0f/src/windows/classes/sun/nio/fs/WindowsWatchService.java =nofollow noreferrer> WindowsWatchService .java 行462
//将文件更改操作转换为watch事件
private WatchEvent.Kind< ?> translateActionToEvent(int action)
{
switch(action){
case FILE_ACTION_MODIFIED:
return StandardWatchEventKinds.ENTRY_MODIFY;
case FILE_ACTION_ADDED:
case FILE_ACTION_RENAMED_NEW_NAME:
return StandardWatchEventKinds.ENTRY_CREATE;
case FILE_ACTION_REMOVED:
case FILE_ACTION_RENAMED_OLD_NAME:
return StandardWatchEventKinds.ENTRY_DELETE;
默认值:
返回null; // action not recognized
}
}
LinuxWatchService.java 第376行
/ **
*将inotify事件映射到WatchEvent.Kind
* /
private WatchEvent.Kind<?> maskToEventKind(int mask){
if((mask& IN_MODIFY)> 0)
return StandardWatchEventKinds.ENTRY_MODIFY;
if((mask& IN_ATTRIB)> 0)
return StandardWatchEventKinds.ENTRY_MODIFY;
if((mask& IN_CREATE)> 0)
return StandardWatchEventKinds.ENTRY_CREATE;
if((mask& IN_MOVED_TO)> 0)
return StandardWatchEventKinds.ENTRY_CREATE;
if((mask& IN_DELETE)> 0)
return StandardWatchEventKinds.ENTRY_DELETE;
if((mask& IN_MOVED_FROM)> 0)
return StandardWatchEventKinds.ENTRY_DELETE;
返回null;
}
I tried to implement simple renaming in java WatchService.
My assumption is: when files are renamed, three operations are performed
- Deletion of file xxx
- creation of file yyy
- modification of file yyy
Below are my codes:
MyWatcher.java
import java.io.IOException;
import java.nio.file.FileSystems;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardWatchEventKinds;
import java.nio.file.WatchEvent;
import java.nio.file.WatchKey;
import java.nio.file.WatchService;
import java.util.ArrayList;
import java.util.List;
public class MyWatcher {
@SuppressWarnings("rawtypes")
public static void main(String[] strings) {
Path myWatchPath = Paths.get("D:\\log4j");
long preventDuplicateTime = 0;
FileDelete onDelete = new FileDelete();//this object must be thread safe
List<String> notifications = new ArrayList<String>();
WatchService myPathWatchService = null;
try {
myPathWatchService = FileSystems.getDefault().newWatchService();
} catch (IOException e) {
e.printStackTrace();
}
try {
myWatchPath.register(myPathWatchService,
StandardWatchEventKinds.ENTRY_CREATE,
StandardWatchEventKinds.ENTRY_DELETE,
StandardWatchEventKinds.ENTRY_MODIFY);
} catch (IOException e) {
e.printStackTrace();
}
boolean isKeyValid = true;
while (isKeyValid) {
WatchKey myPathWatchKey = null;
try {
myPathWatchKey = myPathWatchService.take();
} catch (InterruptedException e) {
e.printStackTrace();// throw
}
for (WatchEvent watchEvent : myPathWatchKey.pollEvents()) {
//WatchEvent.Kind kind = watchEvent.kind();
if (StandardWatchEventKinds.ENTRY_CREATE.equals(watchEvent
.kind())) {
String fileName = watchEvent.context().toString();
if(onDelete.status == -1)
System.out.println("File Created:" + fileName + " "
+ watchEvent.context());
else{
if(onDelete.status == 0){
onDelete.createdTime = System.nanoTime();
if (onDelete.deletedTime / 10000000 == onDelete.createdTime / 10000000) {
onDelete.createdFile = watchEvent.context().toString();
onDelete.status++;
notifications.add("File Created:" + fileName);
}else{
for (String string : notifications) {
System.out.println(string);
}
notifications = new ArrayList<String>();
System.out.println("File Created:" + fileName + " "
+ watchEvent.context());
onDelete = new FileDelete(); //Time duration not close (seems not renamed)
}
}else{
//this should never come here!!
onDelete = new FileDelete();
}
}
}
if (StandardWatchEventKinds.ENTRY_DELETE.equals(watchEvent
.kind())) {
String fileName = watchEvent.context().toString();
if(onDelete.status == -1){
onDelete = new FileDelete();
onDelete.status++;
onDelete.deletedFile = watchEvent.context().toString();
onDelete.deletedTime = System.nanoTime();
notifications.add("File deleted:" + fileName);
}
//System.out.println("File deleted:" + fileName); // push to notfication to array for later use
}
if (StandardWatchEventKinds.ENTRY_MODIFY.equals(watchEvent
.kind())) {
long current = System.nanoTime();
String fileName = watchEvent.context().toString();
if(!(preventDuplicateTime/10000000 == current/10000000))
notifications.add("File modified:" + fileName);
preventDuplicateTime = (System.nanoTime());
onDelete.modifiedFile= fileName;
onDelete.modifiedTime =System.nanoTime();
if(onDelete.status != 1){
for (String messages : notifications) {
System.out.println(messages);
}
onDelete= new FileDelete();
notifications = new ArrayList<String>();
}
else if(onDelete.createdFile.equals(onDelete.modifiedFile))
if( onDelete.createdTime /10000000 == onDelete.modifiedTime/10000000){
System.out.println("File renamed:" + fileName);
onDelete = new FileDelete();
notifications = new ArrayList<String>();
}
}
/*}*/
}
isKeyValid = myPathWatchKey.reset();
}
}
}
FileRename.java
public class FileRename {
int status =-1;
String deletedFile = "";
long deletedTime = 0 ;
String createdFile = "";
long createdTime =0 ;
String modifiedFile = "";
long modifiedTime = 0 ;
}
It's showing perfectly on renaming operations but the problem is I can't figure how to show for onDelete. Because each delete is pushed into notifications!! Or else help me implement renaming!!
*NOTE please don't suggest third party jars! (Since most of them, like JNotify, are OS dependent)
Please find below some explanation why an OS independent solution will not work. And why the event granularity of the Java WatchService
is too weak for what you want to achieve.
Based on the available events (CREATE, MODIFY, DELETE) you cannot determine which action happened.
Take following example on a Linux machine
create some test files
touch /tmp/stackoverflow/foo /tmp/stackoverflow/foo2
execute the following commands
rm foo && cp foo2 bar && echo foo > bar
This will create following events (monitored with WatchDir.java)
ENTRY_DELETE ..:..:.. [.........]: /tmp/stackoverflow/foo
ENTRY_CREATE 20:09:37 [rw.rw.rw.]: /tmp/stackoverflow/bar
ENTRY_MODIFY 20:09:37 [rw.rw.rw.]: /tmp/stackoverflow/bar
Following your assumption of the events order this would have been a rename
action.
Wheras mv bar foobar
creates following events.
ENTRY_DELETE ..:..:.. [.........]: /tmp/stackoverflow/bar
ENTRY_CREATE 19:55:37 [rw.rw.rw.]: /tmp/stackoverflow/foobar
Now the same for a Window machine
create some test files
rem.>c:/temp/stackoverflow/foo
rem.>c:/tmp/stackoverflow/foo2
execute the following commands
del foo
copy foo2 bar
This will create following events
ENTRY_DELETE ..:..:.. [.........]: c:\temp\stackoverflow\foo
ENTRY_CREATE 19:59:10 [.........]: c:\temp\stackoverflow\bar
ENTRY_MODIFY 19:59:10 [.........]: c:\temp\stackoverflow\bar
Following your assumption of the events order this would have been a rename
action.
Wheras ren bar foobar
creates in this case the same events order.
ENTRY_DELETE ..:..:.. [.........]: c:\temp\stackoverflow\bar
ENTRY_CREATE 20:02:41 [.........]: c:\temp\stackoverflow\foobar
ENTRY_MODIFY 20:02:41 [.........]: c:\temp\stackoverflow\foobar
Contrary with iwatch /tmp/stackoverflow/
on a Linux machine you can determine exactly what happen.
Executing the commands rm foo && cp foo2 bar && echo foo > bar
produces following inotify events.
[31/Mär/2015 20:25:40] IN_DELETE /tmp/stackoverflow//foo
[31/Mär/2015 20:25:40] * /tmp/stackoverflow//foo is deleted
[31/Mär/2015 20:25:40] IN_CREATE /tmp/stackoverflow//bar
[31/Mär/2015 20:25:40] IN_CLOSE_WRITE /tmp/stackoverflow//bar
[31/Mär/2015 20:25:40] * /tmp/stackoverflow//bar is closed
[31/Mär/2015 20:25:40] IN_CLOSE_WRITE /tmp/stackoverflow//bar
[31/Mär/2015 20:25:40] * /tmp/stackoverflow//bar is closed
whereas mv bar foobar
creates following inotify events
[31/Mär/2015 20:27:10] IN_MOVED_FROM /tmp/stackoverflow//bar
[31/Mär/2015 20:27:10] IN_MOVED_TO /tmp/stackoverflow//foobar
[31/Mär/2015 20:27:10] * /tmp/stackoverflow//bar is moved to /tmp/stackoverflow//foobar
edit The related Java source code to confirm my explanation. There is no way (in plain Java) to find out if one file is renamed or one file is deleted and another one is created (at the same time).
WindowsWatchService.java line 462
// Translate file change action into watch event
private WatchEvent.Kind<?> translateActionToEvent(int action)
{
switch (action) {
case FILE_ACTION_MODIFIED :
return StandardWatchEventKinds.ENTRY_MODIFY;
case FILE_ACTION_ADDED :
case FILE_ACTION_RENAMED_NEW_NAME :
return StandardWatchEventKinds.ENTRY_CREATE;
case FILE_ACTION_REMOVED :
case FILE_ACTION_RENAMED_OLD_NAME :
return StandardWatchEventKinds.ENTRY_DELETE;
default :
return null; // action not recognized
}
}
LinuxWatchService.java line 376
/**
* map inotify event to WatchEvent.Kind
*/
private WatchEvent.Kind<?> maskToEventKind(int mask) {
if ((mask & IN_MODIFY) > 0)
return StandardWatchEventKinds.ENTRY_MODIFY;
if ((mask & IN_ATTRIB) > 0)
return StandardWatchEventKinds.ENTRY_MODIFY;
if ((mask & IN_CREATE) > 0)
return StandardWatchEventKinds.ENTRY_CREATE;
if ((mask & IN_MOVED_TO) > 0)
return StandardWatchEventKinds.ENTRY_CREATE;
if ((mask & IN_DELETE) > 0)
return StandardWatchEventKinds.ENTRY_DELETE;
if ((mask & IN_MOVED_FROM) > 0)
return StandardWatchEventKinds.ENTRY_DELETE;
return null;
}
这篇关于在java watchservice中实现重命名和删除的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!