Java实现系统目录实时监听更新。
SDK1.7新增的nio WatchService能完美解决这个问题。美中不足是如果部署在window系统下会出现莫名其妙的文件夹占用异常导致子目录监听失效,linux下则完美运行。这个问题着实让人头疼。如果有童鞋找到问题根源请一起探讨。
这里简单的列出用Servlet实现的基本类供大家参考。首先是核心的实现类。
1 |
package com.event;
import static java.nio.file.StandardWatchEventKinds.ENTRY_CREATE;
import static java.nio.file.StandardWatchEventKinds.ENTRY_DELETE;
import static java.nio.file.StandardWatchEventKinds.ENTRY_MODIFY;
import static java.nio.file.StandardWatchEventKinds.OVERFLOW;
import java.io.File;
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.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Observable;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask;
import org.apache.poi.ss.formula.ptg.Ptg;
public class DirectoryWatcher extends Observable {
// 存储监听主键和监听目录的对应
private static Map<WatchKey, String> pathMap = null;
private WatchService watcher;// 目录监听服务
private WatchKey key;// 目录对应的监听key
private Executor executor = Executors.newSingleThreadExecutor();
FutureTask<Integer> task = new FutureTask<Integer>(() -> {
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();
pathMap = new HashMap<WatchKey, String>();
registDirs(dir);
}
private void registDirs(String dir) throws IOException {
List<File> dirs = new ArrayList<File>();
File dirRoot = new File(dir);
getAllDirs(dirRoot, dirs);
// 循环路径下所有目录填充map
for (File eveDir : dirs) {
Path path = Paths.get(eveDir.getAbsolutePath());
// 监控目录内文件的更新、创建和删除事件
try {
key = path.register(watcher, ENTRY_CREATE, ENTRY_DELETE);
pathMap.put(key, eveDir.getAbsolutePath());
} catch (IOException e) {
e.printStackTrace();
}
}
}
// 递归获取所有目录
private void getAllDirs(File dir, List<File> dirs) {
if (dir.isDirectory()) {
dirs.add(dir);
File[] fs = dir.listFiles();
for (File f : fs) {
getAllDirs(f, dirs);
}
}
}
/**
* 启动监控过程
*/
public void execute() {
// 通过线程池启动一个额外的线程加载Watching过程
executor.execute(task);
}
/**
* 关闭后的对象无法重新启动
*/
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();
if (kind == OVERFLOW) {
continue;
}
WatchEvent<Path> ev = cast(event);
Path name = ev.context();
String fileName = name.getFileName().toString();
String dirPath = pathMap.get(signal) + "/" + fileName;
if (kind.name().equals("ENTRY_CREATE")
&& fileName.indexOf(".") == -1) {
Path path = Paths.get(dirPath);
try {
key = path
.register(watcher, ENTRY_CREATE, ENTRY_DELETE);
pathMap.put(key, dirPath);
} catch (IOException e) {
e.printStackTrace();
}
} else if (kind.name().equals("ENTRY_DELETE")
&& fileName.indexOf(".") == -1) {
pathMap.remove(key);
key.cancel();
}
System.out.println("event:" + kind.name() + ";file:" + dirPath);
FileWatchEventArgs args = new FileWatchEventArgs(kind.name(),
pathMap.get(signal), fileName);
notifiy(args);// 通知所有观察者事件变更
}
// 为监控下一个通知做准备
signal.reset();
}
}
// * 通知外部各个Observer目录有新的事件更新
void notifiy(FileWatchEventArgs args) {
// 标注目录已经被做了更改
setChanged();
// 主动通知各个观察者目标对象状态的变更
notifyObservers(args);
}
}
|
来自CODE的代码片
Servlet的实现类:
1 |
package com.demo;
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 javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.entry.VedioFile;
import com.event.DirectoryWatcher;
import com.event.FileWatchEventArgs;
public class GetList extends HttpServlet implements Observer {
private static final long serialVersionUID = 3715567236188574915L;
private String PATH;
private String JsonTreeStr = null;
private static List<VedioFile> vedioFileList;
private static ThreadLocal<String> SubRequestURL = new ThreadLocal<String>();
public static ThreadLocal<String> getSubRequestURL() {
return SubRequestURL;
}
public static List<VedioFile> getVedioFileList() {
return vedioFileList;
}
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
PATH = request.getServletContext().getRealPath("/") + "/curse";
request.getRequestDispatcher("/WEB-INF/index.jsp").forward(request,
response);
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
if (vedioFileList == null) {
PATH = request.getServletContext().getRealPath("/") + "curse";
System.out.println(PATH);
setVedioFileList();
// 建立目录监听,注册观察者为本身
DirectoryWatcher dw = new DirectoryWatcher(PATH);
dw.addObserver(this);
dw.execute();
}
SubRequestURL
.set(request.getRequestURL().toString().split("getList")[0]);
JsonTreeStr = vedioFileList.toString();
response.setContentType("text/xml;charset=utf-8");
response.setHeader("Cache-Control", "no-cache");
response.getWriter().write(JsonTreeStr);
}
@Override
// 实现Observer观察者接口,接收通知
public void update(Observable o, Object arg) {
FileWatchEventArgs args = (FileWatchEventArgs) arg;
String name = args.getName();
String kind = args.getKind();
String dir = args.getDir();
File file = new File(dir + File.separator + name);
if (kind.equals("ENTRY_CREATE")) {
addVedioFile(file);
} else if (kind.equals("ENTRY_DELETE")) {
removeVedioFile(file);
}
}
private void removeVedioFile(File file) {
VedioFile nowVf = getNowVedioFile(file);
if (nowVf != null) {
for (int i = vedioFileList.size() - 1; i >= 0; i--) {
if (nowVf.getPath()
.equals(vedioFileList.get(i).getParentPath())) {
vedioFileList.remove(i);
}
}
vedioFileList.remove(nowVf);
}
}
private VedioFile getNowVedioFile(File file) {
VedioFile nowVf = null;
for (VedioFile vf : vedioFileList) {
if (file.getAbsolutePath().equals(vf.getPath())) {
nowVf = vf;
break;
}
}
return nowVf;
}
private void setVedioFileList() {
File file = new File(PATH);
vedioFileList = new ArrayList<VedioFile>();
getVedioFiles(file, vedioFileList);
}
private void getVedioFiles(File file, List<VedioFile> vfs) {
if (file.isDirectory()) {
File[] files = file.listFiles();
for (File f : files) {
getVedioFiles(f, vfs);
}
}
addVedioFile(file);
}
private void addVedioFile(File file) {
VedioFile vf = new VedioFile();
vf.setParentPath(file.getParent());
vf.setPath(file.getAbsolutePath());
vf.setName(file.getName());
if (file.getName().toLowerCase().endsWith(".mp4")) {
vf.setUrl("vedio?url=" + MD5Utils.md5(vf.getPath()));
}
vedioFileList.add(vf);
}
}
|
来自CODE的代码片
Java实现系统目录实时监听更新。的更多相关文章
- java实时监听日志写入kafka(转)
原文链接:http://www.sjsjw.com/kf_cloud/article/020376ABA013802.asp 目的 实时监听某目录下的日志文件,如有新文件切换到新文件,并同步写入kaf ...
- java实时监听日志写入kafka(多目录)
目的 实时监听多个目录下的日志文件,如有新文件切换到新文件,并同步写入kafka,同时记录日志文件的行位置,以应对进程异常退出,能从上次的文件位置开始读取(考虑到效率,这里是每100条记一次,可调整) ...
- java实时监听日志写入kafka
目的 实时监听某目录下的日志文件,如有新文件切换到新文件,并同步写入kafka,同时记录日志文件的行位置,以应对进程异常退出,能从上次的文件位置开始读取(考虑到效率,这里是每100条记一次,可调整) ...
- js 实时监听input中值变化
注意:用到了jquery需要引入jquery.min.js. 需求: 1.每个地方需要分别打分,总分为100; 2.第一个打分总分为40; 3.第二个打分总分为60. 注意:需要判断null.&quo ...
- Android实时监听网络状态
Android实时监听网络状态(1) 其实手机在网络方面的的监听也比较重要,有时候我们必须实时监控这个程序的实时网络状态,android在网络断开与连接的时候都会发出广播,我们通过接收系统的广播就 ...
- Android几行代码实现实时监听微信聊天
实现效果: 实时监听当前聊天页面的最新一条消息,如图: 实现原理: 同样是利用AccessibilityService辅助服务,关于这个服务类还不了解的同学可以先看下我上一篇关于 ...
- 移动端用js与jquery实时监听输入框值的改动
背景: 在一次移动端H5开发中,需要监听输入框值的实时变动. onchange事件肯定抛弃,因为只能失去焦点才触发. 而keyPress在Android可以触发,iOS不可以. 又不想用Android ...
- javascript --- 实时监听输入框值的变化
实时监听文本框值变化是非常常见的功能,通常最简单的办法就是用keyup,keydown来实现,但是这种方法有两个问题,一个是当直接复制粘贴的时候没法监听到事件,另外一个问题是在移动端,使用删除键删除输 ...
- 实时监听输入框值变化的完美方案:oninput & onpropertychange
实时监听输入框值变化的完美方案:oninput & onpropertychange: 网址:http://www.cnblogs.com/lhb25/archive/2012/11/30/o ...
随机推荐
- python socket练习
服务器端 #服务器端 import socket server = socket.socket() server.bind(('localhost',6969))#绑定要监听的端口 server.li ...
- 【原创】SQL SERVER 2012安装配置说明(多图详解)
1. 优先安装软件 1. net framework3.5. 2. 在安装SQL SERVER 2012前需要3.5的支持.在WIN 2012系统可以在系统管理的添加角色和功能中安装,如下将[.NET ...
- EasyUI手风琴 Tab卡使用
--案例项目代码,初始化手风琴,定义打开Tab的方法. $(result).each(function () { //m_pi_jscode,pi_jscode if (m_pi_id != this ...
- gcc编译流程
gcc的编译流程分为四个步骤,分别为: 预处理(Pre-Processing) 编译(Compiling) 汇编(Assembling) 链接(Linking) 以hello.c为例子,在这四个步骤中 ...
- ABI (应用程序二进制接口)
http://baike.baidu.com/link?url=ybErtZo0zAB5_P-kKZmT_nd9FdxaAAPqW4jqtwN5Tmu_q8weayOOFOJBrXw1RLbR20sK ...
- CoreData的学习
第一步:创建项目是勾选coredata,当然创建的时候没有勾选,之后还可以手动生产, 然后:创建数据库模型,及为其添加模型的属性. 然后生成模型文件: 注意⚠️:首先设置为Manual/None 不 ...
- 2017.9.27 JavaWeb 属性的设置和获取
3.4.3新属性的设置和获取 对于getpParamter方法是通过参数传递获得数据, 设置数据的方法格式: void request.setAttribute("key",Ob ...
- FastRCNN 训练自己数据集 (1编译配置)
http://www.cnblogs.com/louyihang-loves-baiyan/p/4885659.html 按照博客的教程配置,但自己在服务器上配置时,USE_CUDNN = 1会报错, ...
- 通过ODBC接口访问人大金仓数据库
国产化软件和国产化芯片的窘境一样,一方面市场已经存在性能优越的同类软件,成本很低,但小众的国产化软件不仅需要高价买入版权,并且软件开发维护成本高:另一方面,国产软件目前普遍难用,性能不稳定,Bug ...
- P2096 最佳旅游线路
最大字段和加贪心 算长了个见识吧 #include<iostream> #include<cstdio> #include<algorithm> using nam ...