项目需求:实时监控ftp服务器中指定目录的文件新增情况,并做统计。
刚接到这个需求,第一反应是直接获取指定File下的所有文件,相隔很短时间再去扫描一次。两次扫描文件一比较,即可知道文件的新增、变化、删除。但本着不重复造轮子的想法,以及为避免很多未预知的错误,在网上一搜,发现有现成的框架,便直接拿来使用(分析源码,大体思路也是如此)。话不多说,直接粘代码。
首先导入maven依赖:
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>${commons-io.version}</version>
</dependency>
代码如下:
String monitorPath ="";
//扫描间隔
interval = 1000;
//interval = Long.valueOf(PropertiesUtils.getPropertie("monitor.local.interval"));
// 创建过滤器
//IOFileFilter directories = FileFilterUtils.and(
//FileFilterUtils.directoryFileFilter(),
//HiddenFileFilter.VISIBLE);
//筛选文件后缀
//IOFileFilter ioFileFilter = FileFilterUtils.suffixFileFilter(".xls");
IOFileFilter files = FileFilterUtils.and(
FileFilterUtils.fileFileFilter(),
FileFilterUtils.suffixFileFilter(""));
//此处只对文件进行监控
IOFileFilter filter = FileFilterUtils.or(files);
//过滤器
FileAlterationObserver observer = new FileAlterationObserver(new File(monitorPath), filter);
//不使用过滤器
//FileAlterationObserver observer = new FileAlterationObserver(new File(rootDir));
observer.addListener(new FileListener());
//创建文件变化监听器
FileAlterationMonitor monitor = new FileAlterationMonitor(interval, observer);
//开始监控(FileAlterationMonitor 实现了runnable)
monitor.start();
其中FileListener是自定义的类,实现了FileAlterationListenerAdaptor接口。
这个可以自定义文件/目录 create、update、delete时的业务逻辑,具体逻辑自行定义。
public class FileListener extends FileAlterationListenerAdaptor {
private Logger logger = LoggerFactory.getLogger(FileListener.class);
/**
* 文件创建
*/
@Override
public void onFileCreate(File file) {
logger.info("[create]:" + file.getAbsolutePath());
// FileWorker.exec(file);
}
/**
* 文件修改
*/
public void onFileChange(File file) {
logger.info("[onFileChange]: _"+ file.getAbsolutePath());
}
/**
* 文件删除
*/
public void onFileDelete(File file) {
logger.info("[onFileDelete]:" + file.getAbsolutePath());
}
/**
* 目录创建
*/
public void onDirectoryCreate(File directory) {
logger.info("[onDirectoryCreate]:" + directory.getAbsolutePath());
}
/**
* 目录修改
*/
public void onDirectoryChange(File directory) {
}
/**
* 目录删除
*/
public void onDirectoryDelete(File directory) {
}
public void onStart(FileAlterationObserver observer) {
// TODO Auto-generated method stub
super.onStart(observer);
}
public void onStop(FileAlterationObserver observer) {
// TODO Auto-generated method stub
super.onStop(observer);
}
但此处运行代码发现有几个问题:
1、该扫描是单线程操作的。如果文件监控的业务逻辑处理比较耗时,整个操作会特别耗时间;
2、扫描时文件的大小问题(windows下,文件一旦创建,大小即固定;linux则是文件大小从0开始增长)。这就导致ftp还未完全上传完文件,导致此时文件大小获取错误。
结合项目实际,我创建了无界队列的线程池(没有特别大的文件量)。即每次有新增文件,将File传入线程池去执行,确保ftp文件完整。确保文件完整的逻辑并不复杂,只需多次比较文件的大小、修改时间。
代码不多,如下:
class MyThread extends Thread{
private File file;
public MyThread() {
}
public MyThread(File file) {
this.file = file;
}
@Override
public void run() {
long size = 0;
long lastModified = 0;
int count = 0;
FileInsertService.getInstance().insertFile(file);
while (file.exists()) {
// 避免短暂的网络阻塞
if (size == file.length() && lastModified == file.lastModified()) {
if (++count >= 2) {
break;
}
} else {
size = file.length();
lastModified = file.lastModified();
}
try {
Thread.sleep(500);
} catch (InterruptedException e) {
return;
}
}
//TODO 自己的业务逻辑
}
}