Java监控文件夹或文件的变动

Java监控文件夹或文件的变动

很多时候我们需要对文件夹或者文件进行监控,已观察其是否被删除、创建或者修改,然后根据文件的行为进行相应的后续处理,以往的话多数是通过轮询去观察文件夹或者文件的变动,这样肯定会经过磁盘的I/O的操作,大大的降低了效率,而且不能及时的响应,如果要提升响应速度必定会增加磁盘的占用,不是明智之举。

以往的文件夹监控流程如下:

JavaSE 1.7提供了相关的API,去监视文件或者文件夹的变动,主要的API都在java.nio.file下面,其大概流程如下:

Demo下载

http://pan.baidu.com/s/1dDpTAAd

 代码 

监控主类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
package  org.xdemo.superutil.j2se.filewatch;
 
import  static  java.nio.file.LinkOption.NOFOLLOW_LINKS;
 
import  java.io.File;
import  java.io.IOException;
import  java.nio.file.FileSystems;
import  java.nio.file.FileVisitResult;
import  java.nio.file.Files;
import  java.nio.file.Path;
import  java.nio.file.Paths;
import  java.nio.file.SimpleFileVisitor;
import  java.nio.file.StandardWatchEventKinds;
import  java.nio.file.WatchEvent;
import  java.nio.file.WatchEvent.Kind;
import  java.nio.file.WatchKey;
import  java.nio.file.WatchService;
import  java.nio.file.attribute.BasicFileAttributes;
import  java.util.HashMap;
import  java.util.Map;
 
/**
  * 文件夹监控
 
  * @author Goofy <a href="http://www.xdemo.org/">http://www.xdemo.org/</a>
  * @Date 2015年7月3日 上午9:21:33
  */
public  class  WatchDir {
 
     private  final  WatchService watcher;
     private  final  Map<WatchKey, Path> keys;
     private  final  boolean  subDir;
 
     /**
      * 构造方法
     
      * @param file
      *            文件目录,不可以是文件
      * @param subDir
      * @throws Exception
      */
     public  WatchDir(File file,  boolean  subDir, FileActionCallback callback)  throws  Exception {
         if  (!file.isDirectory())
             throw  new  Exception(file.getAbsolutePath() +  "is not a directory!" );
 
         this .watcher = FileSystems.getDefault().newWatchService();
         this .keys =  new  HashMap<WatchKey, Path>();
         this .subDir = subDir;
 
         Path dir = Paths.get(file.getAbsolutePath());
 
         if  (subDir) {
             registerAll(dir);
         else  {
             register(dir);
         }
         processEvents(callback);
     }
 
     @SuppressWarnings ( "unchecked" )
     static  <T> WatchEvent<T> cast(WatchEvent<?> event) {
         return  (WatchEvent<T>) event;
     }
 
     /**
      * 观察指定的目录
     
      * @param dir
      * @throws IOException
      */
     private  void  register(Path dir)  throws  IOException {
         WatchKey key = dir.register(watcher, StandardWatchEventKinds.ENTRY_CREATE, StandardWatchEventKinds.ENTRY_DELETE, StandardWatchEventKinds.ENTRY_MODIFY);
         keys.put(key, dir);
     }
 
     /**
      * 观察指定的目录,并且包括子目录
      */
     private  void  registerAll( final  Path start)  throws  IOException {
         Files.walkFileTree(start,  new  SimpleFileVisitor<Path>() {
             @Override
             public  FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs)  throws  IOException {
                 register(dir);
                 return  FileVisitResult.CONTINUE;
             }
         });
     }
 
     /**
      * 发生文件变化的回调函数
      */
     @SuppressWarnings ( "rawtypes" )
     void  processEvents(FileActionCallback callback) {
         for  (;;) {
             WatchKey key;
             try  {
                 key = watcher.take();
             catch  (InterruptedException x) {
                 return ;
             }
             Path dir = keys.get(key);
             if  (dir ==  null ) {
                 System.err.println( "操作未识别" );
                 continue ;
             }
 
             for  (WatchEvent<?> event : key.pollEvents()) {
                 Kind kind = event.kind();
 
                 // 事件可能丢失或遗弃
                 if  (kind == StandardWatchEventKinds.OVERFLOW) {
                     continue ;
                 }
 
                 // 目录内的变化可能是文件或者目录
                 WatchEvent<Path> ev = cast(event);
                 Path name = ev.context();
                 Path child = dir.resolve(name);
                 File file = child.toFile();
                 if  (kind.name().equals(FileAction.DELETE.getValue())) {
                     callback.delete(file);
                 else  if  (kind.name().equals(FileAction.CREATE.getValue())) {
                     callback.create(file);
                 else  if  (kind.name().equals(FileAction.MODIFY.getValue())) {
                     callback.modify(file);
                 else  {
                     continue ;
                 }
 
                 // if directory is created, and watching recursively, then
                 // register it and its sub-directories
                 if  (subDir && (kind == StandardWatchEventKinds.ENTRY_CREATE)) {
                     try  {
                         if  (Files.isDirectory(child, NOFOLLOW_LINKS)) {
                             registerAll(child);
                         }
                     catch  (IOException x) {
                         // ignore to keep sample readbale
                     }
                 }
             }
 
             boolean  valid = key.reset();
             if  (!valid) {
                 // 移除不可访问的目录
                 // 因为有可能目录被移除,就会无法访问
                 keys.remove(key);
                 // 如果待监控的目录都不存在了,就中断执行
                 if  (keys.isEmpty()) {
                     break ;
                 }
             }
         }
     }
 
}

文件变更行为枚举

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package  org.xdemo.superutil.j2se.filewatch;
 
/**
  * 文件变动行为枚举
  * @author Goofy <a href="http://www.xdemo.org/">http://www.xdemo.org/</a>
  * @Date 2015年7月3日 上午10:40:04
  */
public  enum  FileAction {
     DELETE( "ENTRY_DELETE" ), CREATE( "ENTRY_CREATE" ), MODIFY( "ENTRY_MODIFY" );
     private  String value;
 
     FileAction(String value) {
         this .value = value;
     }
 
     public  String getValue() {
         return  value;
     }
}

文件变动回调方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package  org.xdemo.superutil.j2se.filewatch;
 
import  java.io.File;
 
/**
  * 文件操作的回调方法
  * @author Goofy <a href="http://www.xdemo.org/">http://www.xdemo.org/</a>
  * @Date 2015年7月6日 上午11:11:34
  */
public  abstract  class  FileActionCallback {
 
     public  void  delete(File file) {
     };
 
     public  void  modify(File file) {
     };
 
     public  void  create(File file) {
     };
 
}

 用法 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
package  org.xdemo.superutil.j2se.filewatch;
 
import  java.io.File;
 
/**
 
  * @author Goofy
  * @Date 2015年7月6日 上午11:22:32
  */
public  class  Usage {
 
     public  static  void  main(String[] args)  throws  Exception {
         final  File file =  new  File( "D:\\upload" );
         new  Thread( new  Runnable() {
             @Override
             public  void  run() {
                 try  {
                     new  WatchDir(file,  true new  FileActionCallback() {
                         @Override
                         public  void  create(File file) {
                             System.out.println( "文件已创建\t"  + file.getAbsolutePath());
                         }
 
                         @Override
                         public  void  delete(File file) {
                             System.out.println( "文件已删除\t"  + file.getAbsolutePath());
                         }
 
                         @Override
                         public  void  modify(File file) {
                             System.out.println( "文件已修改\t"  + file.getAbsolutePath());
                         }
                     });
                 catch  (Exception e) {
                     e.printStackTrace();
                 }
             }
         }).start();
 
         System.out.println( "正在监视文件夹:"  + file.getAbsolutePath() +  "的变化" );
     }
 
}

 截图 

 说明 

修改文件名称会产生多个事件

    待修改文件>删除该文件->创建该文件->修改该文件

    对于子文件夹的内容变动,也会引起该子文件夹的修改事件


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值