Java文件监控方式(转载)

有三种方式:
1、java common.io 内部实现是遍历的方式,小文件夹的效率还好,比如我测试60G的目录,就很慢很慢了。
2、jdk 7 的watch service //没有测试
3、jnotify 直接调用windows的api,效率很高,也很简单,推荐使用。

------------------------------------------------------------------------------------------------------------------------------------------------------------
common.io
需要java.io 2.1及其以上版本。版本地址如下:
http://commons.apache.org/io/download_io.cgi

import org.apache.commons.io.monitor.FileAlterationListenerAdaptor;

/**
* 自定义文件监听器
* @author
* @date 2010-11-16
* @file org.demo.file.MyFileListener.java
*/
public class MyFileListener extends FileAlterationListenerAdaptor{
@Override
public void onFileCreate(File file) {
System.out.println("[新建]:" + file.getAbsolutePath());
}
@Override
public void onFileChange(File file) {
System.out.println("[修改]:" + file.getAbsolutePath());
}
@Override
public void onFileDelete(File file) {
System.out.println("[删除]:" + file.getAbsolutePath());
}
}


import org.apache.commons.io.monitor.FileAlterationMonitor;
import org.apache.commons.io.monitor.FileAlterationObserver;


public class Test {

/**
* @param args
*/
public static void main(String[] args) throws Exception{
// 监控目录
String rootDir = "d:\\Temp";
// 轮询间隔 5 毫秒
long interval = 5l;
//
FileAlterationObserver observer = new FileAlterationObserver(
rootDir,null,
null);
observer.addListener(new MyFileListener());
FileAlterationMonitor monitor = new FileAlterationMonitor(interval,observer);
// 开始监控
monitor.start();
}
}
------------------------------------------------------------------------------------------------------------------------------------------------------------
jdk7 watchservice
详见 http://www.cnblogs.com/callwangxiang/archive/2011/08/04/JavaDirectoryWatcherFileWatcher.html
Java SE 7 Tutorial中增加了一个监控目录变更情况的示例,用于介绍其新发布的WatchService API。



但对于用惯了.NET FileWatcher的用户而言,如果用于项目我认为它有两个欠缺:

1、应该提供一个独立线程后台运行机制,让这个监控过程自己在后台转,不影响前端处理

2、 Java不像.NET有内置的源生事件机制,不过可以借助它内置的Observer/Observable对象用观察者模式实现准事件



下面是把Java SE Tutorial示例中无关内容删除,补充上述两个扩展后的实现,因为这个API比较新,也希望能和大家多多探讨:



1、参考.NET定义事件参数对象

package marvellousworks.practicalpattern.concept.unittest;

import java.nio.file.WatchEvent.Kind;

/**
* 文件系统事件类型
* @author wangxiang
*
*/
public final class FileSystemEventArgs {
private final String fileName;
private final Kind<?> kind;

public FileSystemEventArgs(String fileName, Kind<?> kind){
this.fileName = fileName;
this.kind = kind;
}

/**
* 文件的路径
*/
public String getFileName(){return fileName;}

/**
* 操作类型:变更、创建、删除
*/
@SuppressWarnings("rawtypes")
public Kind getKind(){return kind;}
}


2、定义DirectoryWatcher,用于监控某个文件夹,至于如何扩展FileWatcher则可以在这个基础上通过限定文件名称和操作类型的方式扩展



package marvellousworks.practicalpattern.concept.unittest;

import java.io.IOException;
import java.nio.file.FileSystems;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.WatchEvent;
import java.nio.file.WatchEvent.Kind;
import java.nio.file.WatchKey;
import java.nio.file.WatchService;
import java.util.Observable;
import java.util.concurrent.Callable;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask;

import static java.nio.file.StandardWatchEventKinds.*;

/**
* 监控一个目录内文件的更新、创建和删除事件(不包括子目录)
*
* 对于http://download.oracle.com/javase/tutorial/essential/io/notification.html进行了改造
* 使其更接近.NET的DirectoryWatcher使用习惯
*
* 由于java没有类似.NET源生的事件机制
* 因此实现上采用了Java SE自带的Observer/Observable对象对外抛出“假”事件
*
* 适于Java SE 7
*
* @author wangxiang
*
*/
public class DirectoryWatcher extends Observable{

private WatchService watcher;
private Path path;
private WatchKey key;
private Executor executor = Executors.newSingleThreadExecutor();

FutureTask<Integer> task = new FutureTask<Integer>(
new Callable<Integer>(){
public Integer call() throws InterruptedException{
processEvents();
return Integer.valueOf(0);}});

@SuppressWarnings("unchecked")
static <T> WatchEvent<T> cast(WatchEvent<?> event) {
return (WatchEvent<T>) event;
}

public DirectoryWatcher(String dir) throws IOException {
watcher = FileSystems.getDefault().newWatchService();
path = Paths.get(dir);
// 监控目录内文件的更新、创建和删除事件
key = path.register(watcher, ENTRY_MODIFY, ENTRY_CREATE, ENTRY_DELETE);
}

/**
* 启动监控过程
*/
public void execute(){
// 通过线程池启动一个额外的线程加载Watching过程
executor.execute(task);
}

/**
* 关闭后的对象无法重新启动
* @throws IOException
*/
public void shutdown() throws IOException {
watcher.close();
executor = null;
}

/**
* 监控文件系统事件
*/
void processEvents() {
while (true) {
// 等待直到获得事件信号
WatchKey signal;
try {
signal = watcher.take();
} catch (InterruptedException x) {
return;
}

for (WatchEvent<?> event : signal.pollEvents()) {
Kind<?> kind = event.kind();

// TBD - provide example of how OVERFLOW event is handled
if (kind == OVERFLOW) {
continue;
}

// Context for directory entry event is the file name of entry
WatchEvent<Path> ev = cast(event);
Path name = ev.context();
notifiy(name.getFileName().toString(), kind);
}
// 为监控下一个通知做准备
key.reset();
}
}

/**
* 通知外部各个Observer目录有新的事件更新
*/
void notifiy(String fileName, Kind<?> kind){
// 标注目录已经被做了更改
setChanged();
// 主动通知各个观察者目标对象状态的变更
// 这里采用的是观察者模式的“推”方式
notifyObservers(new FileSystemEventArgs(fileName, kind));
}
}


3、单元测试

package marvellousworks.practicalpattern.concept.unittest;

import static org.junit.Assert.*;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Observable;
import java.util.Observer;

import org.junit.Test;
import static java.nio.file.StandardWatchEventKinds.*;

public class DirectoryWatcherFixture {

private static final String DIR_PATH =System.getProperty("user.dir");
private static final File DIR = new File(DIR_PATH);
private static final String SUFFIX = ".txt";
private static final String PREFIX = "test";
private static final int ADD_TIMES = 3;

/**
* 观察者
* @author wangxiang
*
*/
public class Logger implements Observer{
@Override
public void update(Observable observable, Object eventArgs) {
FileSystemEventArgs args = (FileSystemEventArgs) eventArgs;
System.out.printf("%s has been %s\n", args.getFileName(), args.getKind());
assertTrue(args.getFileName().startsWith(PREFIX));
assertEquals(ENTRY_CREATE, args.getKind());
}
}

@Test
public void testWatchFile() throws IOException, InterruptedException{
DirectoryWatcher watcher = new DirectoryWatcher(DIR_PATH);
Logger l1 = new Logger();
watcher.addObserver(l1);
watcher.execute();

// 创建一系列临时文件
List<String> files = new ArrayList<>();
for(int i=0; i<ADD_TIMES; i++){
files.add(File.createTempFile(PREFIX, SUFFIX, DIR).toString());
}

// 延迟等待后台任务的执行
Thread.sleep(4000);
watcher.shutdown();
System.out.println("finished");
}
}


Console窗口显示的测试内容



test5769907807190550725.txt has been ENTRY_CREATE
test4657672246246330348.txt has been ENTRY_CREATE
test1823102943601166149.txt has been ENTRY_CREATE
finished

------------------------------------------------------------------------------------------------------------------------------------------------------------
jnotify
详见 http://www.blogjava.net/pengo/archive/2011/01/09/342622.html
jnotify分为64位 和 32位。 详见 http://jnotify.sourceforge.net/
public class Test {

/**
* @param args
*/
public static void main(String[] args) throws Exception{
// 监控目录
String rootDir = "Z:\\";
String path = rootDir;
int mask = JNotify.FILE_CREATED | JNotify.FILE_DELETED
| JNotify.FILE_MODIFIED | JNotify.FILE_RENAMED;
boolean watchSubtree = true;
while (true) { //否则一次就完了
System.out.println("begin watch");
JNotify.addWatch(path, mask, watchSubtree, new MyJnotifyListner());
Thread.sleep(10000);
System.out.println("end watch");
}
}

}


import net.contentobjects.jnotify.JNotifyListener;


public class MyJnotifyListner implements JNotifyListener {
public void fileRenamed(int wd, String rootPath, String oldName,
String newName) {
System.out.println("文件:" + rootPath + " : " + oldName + " 重命名为: "
+ newName + "\n");
}

public void fileModified(int wd, String rootPath, String name) {
System.out.println("文件修改 " + rootPath + " : " + name + "\n");
}

public void fileDeleted(int wd, String rootPath, String name) {
System.out.println("删除文件: " + rootPath + " : " + name + "\n");
}

public void fileCreated(int wd, String rootPath, String name) {
System.out.println("新建文件: " + rootPath + " : " + name + "\n");
}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值