怎么用Java NIO观看文件
这篇文章给大家分享的用Java NIO观看文件的方法,相信大部分人都还没学会这个技能,为了让大家学会,给大家总结了以下内容,话不多说,一起往下看吧。
该java.nio.file包提供了一个文件更改通知API,被称为手表服务API。它使我们能够在监视服务中注册文件夹。注册时,我们告诉服务我们感兴趣的事件类型是:文件创建,文件修改或文件删除。
当服务检测到感兴趣的事件时,会将其转发到注册的进程并根据需要进行处理。基本上是这样的:
1.第一步是WatchService使用类的newWatchService()方法创建一个新的FileSystem。
2.接下来,我们使用Path要关注的事件类型为要监视的文件夹注册一个实例。
3.最后,我们实现了一个无限循环来等待传入事件。当事件发生时,该密钥会发出信号,并放入观察者的队列中。处理完事件后,我们需要ready通过调用其reset()方法将其放回状态。如果返回false,则密钥不再有效,并且循环可以退出。
4.15.WatchService watchService = FileSystems.getDefault().newWatchService();6.27.Path path = Paths.get("c:\\directory");8.39.path.register(watchService, ENTRY_CREATE, ENTRY_MODIFY, ENTRY_DELETE);10.411.boolean poll = true;12.513.while (poll) {14.615. WatchKey key = watchService.take();16.717. for (WatchEvent<?> event : key.pollEvents()) {18.819. System.out.println("Event kind : " + event.kind() + " - File : " + event.context());20.921. }22.1023. poll = key.reset();24.1125.}
这是控制台输出:
1Event kind : ENTRY_CREATE - File : file.txt2Event kind : ENTRY_DELETE - File : file.txt3Event kind : ENTRY_CREATE - File : test.txt4Event kind : ENTRY_MODIFY - File : test.txt
WatchService API的级别很低,允许我们对其进行自定义。在本文中,按照Observer模式,我们将在此机制之上设计一个高级API,以侦听给定文件夹的文件事件。我们将从创建一个FileEvent类开始,该类扩展了java.util.EventObject所有事件状态对象的来源。甲FileEvent实例被构造为具有到源,这是逻辑上在其上事件发生在所述文件的引用。
FileEvent.java
import java.io.File;2import java.util.EventObject;34public class FileEvent extends EventObject {56 public FileEvent(File file) {7 super(file);8 }910 public File getFile() {11 return (File) getSource();12 }1314}
接下来,我们创建一个FileListener必须由观察者实现的接口,以便在发生文件事件时得到通知。它扩展了java.util.EventListener接口,这是所有事件侦听器接口都必须扩展的标记接口。
FileListener.java
import java.util.EventListener;23public interface FileListener extends EventListener {45 public void onCreated(FileEvent event);67 public void onModified(FileEvent event);89 public void onDeleted(FileEvent event);1011}
难题的最后一步是创建主题,该主题维护观察者列表,并通过调用其方法之一来通知状态更改。我们将其命名FileWatcher并提供一个文件夹,这就是构造此类实例的方式。
1public class FileWatcher {23 protected List<FileListener> listeners = new ArrayList<>();4 protected final File folder;56 public FileWatcher(File folder) {7 this.folder = folder;8 }910 public List<FileListener> getListeners() {11 return listeners;12 }1314 public FileWatcher setListeners(List<FileListener> listeners) {15 this.listeners = listeners;16 return this;17 }1819}
它可以实现该Runnable接口,因此,watch()如果文件夹存在,则在调用其方法时可以使用守护程序线程启动监视进程。
public class FileWatcher implements Runnable {23 public void watch() {4 if (folder.exists()) {5 Thread thread = new Thread(this);6 thread.setDaemon(true);7 thread.start();8 }9 }1011 @Override12 public void run() {13 // implementation not yet provided14 }1516}
在其run()方法的实现中,将WatchService创建一个实例以轮询try-with-resources语句中的事件。我们将使用类中的静态最终列表来对其进行跟踪FileWatcher,因此我们稍后可以调用其close()方法,以使任何等待检索键的线程抛出未选中的对象ClosedWatchServiceException,这将以干净的方式中断监视过程。因此,当正常关闭应用程序时,我们不会收到内存泄漏警告。
public class FileWatcher implements Runnable {23 public void watch() {4 if (folder.exists()) {5 Thread thread = new Thread(this);6 thread.setDaemon(true);7 thread.start();8 }9 }1011 @Override12 public void run() {13 // implementation not yet provided14 }1516}1public class FileWatcher implements Runnable {23 protected static final List<WatchService> watchServices = new ArrayList<>();45 @Override6 public void run() {7 try (WatchService watchService = FileSystems.getDefault().newWatchService()) {8 Path path = Paths.get(folder.getAbsolutePath());9 path.register(watchService, ENTRY_CREATE, ENTRY_MODIFY, ENTRY_DELETE);10 watchServices.add(watchService);11 boolean poll = true;12 while (poll) {13 poll = pollEvents(watchService);14 }15 } catch (IOException | InterruptedException | ClosedWatchServiceException e) {16 Thread.currentThread().interrupt();17 }18 }1920 protected boolean pollEvents(WatchService watchService) throws InterruptedException {21 WatchKey key = watchService.take();22 Path path = (Path) key.watchable();23 for (WatchEvent<?> event : key.pollEvents()) {24 notifyListeners(event.kind(), path.resolve((Path) event.context()).toFile());25 }26 return key.reset();27 }2829 public static List<WatchService> getWatchServices() {30 return Collections.unmodifiableList(watchServices);31 }3233}
每当发生事件时,文件路径都会被解析,并相应地通知侦听器。如果是创建新文件夹,FileWatcher则将创建另一个实例进行监视。
public class FileWatcher implements Runnable {23 protected void notifyListeners(WatchEvent.Kind<?> kind, File file) {4 FileEvent event = new FileEvent(file);5 if (kind == ENTRY_CREATE) {6 for (FileListener listener : listeners) {7 listener.onCreated(event);8 }9 if (file.isDirectory()) {10 // create a new FileWatcher instance to watch the new directory11 new FileWatcher(file).setListeners(listeners).watch();12 }13 } 14 else if (kind == ENTRY_MODIFY) {15 for (FileListener listener : listeners) {16 listener.onModified(event);17 }18 }19 else if (kind == ENTRY_DELETE) {20 for (FileListener listener : listeners) {21 listener.onDeleted(event);22 }23 }24 }2526}
这是FileWatcher该类的完整清单。
FileWatcher.javaimport static java.nio.file.StandardWatchEventKinds.*;2import java.io.File;3import java.io.IOException;4import java.nio.file.ClosedWatchServiceException;5import java.nio.file.FileSystems;6import java.nio.file.Path;7import java.nio.file.Paths;8import java.nio.file.WatchEvent;9import java.nio.file.WatchKey;10import java.nio.file.WatchService;11import java.util.ArrayList;12import java.util.Collections;13import java.util.List;1415public class FileWatcher implements Runnable {1617 protected List<FileListener> listeners = new ArrayList<>();18 protected final File folder;19 protected static final List<WatchService> watchServices = new ArrayList<>();20 21 public FileWatcher(File folder) {22 this.folder = folder;23 }2425 public void watch() {26 if (folder.exists()) {27 Thread thread = new Thread(this);28 thread.setDaemon(true);29 thread.start();30 }31 }3233 @Override34 public void run() {35 try (WatchService watchService = FileSystems.getDefault().newWatchService()) {36 Path path = Paths.get(folder.getAbsolutePath());37 path.register(watchService, ENTRY_CREATE, ENTRY_MODIFY, ENTRY_DELETE);38 watchServices.add(watchService);39 boolean poll = true;40 while (poll) {41 poll = pollEvents(watchService);42 }43 } catch (IOException | InterruptedException | ClosedWatchServiceException e) {44 Thread.currentThread().interrupt();45 }46 }4748 protected boolean pollEvents(WatchService watchService) throws InterruptedException {49 WatchKey key = watchService.take();50 Path path = (Path) key.watchable();51 for (WatchEvent<?> event : key.pollEvents()) {52 notifyListeners(event.kind(), path.resolve((Path) event.context()).toFile());53 }54 return key.reset();55 }5657 protected void notifyListeners(WatchEvent.Kind<?> kind, File file) {58 FileEvent event = new FileEvent(file);59 if (kind == ENTRY_CREATE) {60 for (FileListener listener : listeners) {61 listener.onCreated(event);62 }63 if (file.isDirectory()) {64 new FileWatcher(file).setListeners(listeners).watch();65 }66 } 67 else if (kind == ENTRY_MODIFY) {68 for (FileListener listener : listeners) {69 listener.onModified(event);70 }71 }72 else if (kind == ENTRY_DELETE) {73 for (FileListener listener : listeners) {74 listener.onDeleted(event);75 }76 }77 }7879 public FileWatcher addListener(FileListener listener) {80 listeners.add(listener);81 return this;82 }8384 public FileWatcher removeListener(FileListener listener) {85 listeners.remove(listener);86 return this;87 }8889 public List<FileListener> getListeners() {90 return listeners;91 }9293 public FileWatcher setListeners(List<FileListener> listeners) {94 this.listeners = listeners;95 return this;96 }9798 public static List<WatchService> getWatchServices() {99 return Collections.unmodifiableList(watchServices);100 }101102}
我们设计的最后一点就是创建一个FileAdapter类,该类提供了FileListener接口的默认实现,因此我们只能处理很少的事件来保存代码。
FileAdapter.javapublic abstract class FileAdapter implements FileListener {23 @Override4 public void onCreated(FileEvent event) {5 // no implementation provided6 }78 @Override9 public void onModified(FileEvent event) {10 // no implementation provided11 }1213 @Override14 public void onDeleted(FileEvent event) {15 // no implementation provided16 }1718}
该FileAdapter班是在我的情况是非常有用的,发展我的IDE中的Servlet应用程序时重新加载Groovy脚本。修改文件并将其重新发布到部署目录中后,首先将其删除,然后再重新创建。因此,可以在Windows平台上触发两次的修改事件可以忽略,而在我的上下文中,删除事件是不可用的。
这是因为,当前,我们无法从Web容器中注销Servlet,过滤器或侦听器。因此,我发现没有理由在生产中启用该功能。同样,在这种使用情况下,性能也不是问题,因为很难让五个FileWatcher实例被另一个实例监视。
protected void loadScripts(File folder) {2 if (folder.exists()) {3 File[] files = folder.listFiles();4 if (files != null) {5 for (File file : files) {6 if (file.isFile()) {7 Object object = scriptManager.loadScript(file);8 register(object);9 } else {10 loadScripts(file);11 }12 }13 }14 watch(folder);15 }16}1718protected void watch(File folder) {19 new FileWatcher(folder).addListener(new FileAdapter() {20 @Override21 public void onCreated(FileEvent event) {22 File file = event.getFile();23 if (file.isFile()) {24 logger.info("processing script " + file.getName());25 process(file);26 }27 }28 }).watch();29}3031protected void process(File script) {32 Object object = scriptManager.loadScript(script);33 // update the application accordingly 34}
尽管据说Thread.sleep()在单元测试中使用该方法通常不是一个好主意,但FileWatcher由于需要在操作之间进行延迟,因此我们仍将使用它为该类编写测试用例。
import static org.junit.Assert.*;2import java.io.File;3import java.io.FileWriter;4import java.io.IOException;5import java.util.HashMap;6import java.util.Map;7import org.junit.Test;89public class FileWatcherTest {1011 @Test12 public void test() throws IOException, InterruptedException {13 File folder = new File("src/test/resources");14 final Map<String, String> map = new HashMap<>();15 FileWatcher watcher = new FileWatcher(folder);16 watcher.addListener(new FileAdapter() {17 public void onCreated(FileEvent event) {18 map.put("file.created", event.getFile().getName());19 }20 public void onModified(FileEvent event) {21 map.put("file.modified", event.getFile().getName());22 }23 public void onDeleted(FileEvent event) {24 map.put("file.deleted", event.getFile().getName());25 }26 }).watch();27 assertEquals(1, watcher.getListeners().size());28 wait(2000);29 File file = new File(folder + "/test.txt");30 try(FileWriter writer = new FileWriter(file)) {31 writer.write("Some String");32 }33 wait(2000);34 file.delete();35 wait(2000);36 assertEquals(file.getName(), map.get("file.created"));37 assertEquals(file.getName(), map.get("file.modified"));38 assertEquals(file.getName(), map.get("file.deleted"));39 }4041 public void wait(int time) throws InterruptedException {42 Thread.sleep(time);43 }4445}46
对于我来说,这可能是纠正其实现的绝好机会,Class.newInstance()方法是将不推荐使用的方法替换为该Class.getConstructor().newInstance()方法,以使其正确无误。
import java.io.File;2import java.net.URL;3import groovy.util.GroovyScriptEngine;45public class ScriptManager {67 protected final GroovyScriptEngine engine;89 public ScriptManager(File folder) {10 engine = createScriptEngine(folder);11 }1213 protected GroovyScriptEngine createScriptEngine(File folder) {14 URL[] urls = { folder.toURI().toURL() };15 return new GroovyScriptEngine(urls, this.getClass().getClassLoader());16 }1718 public Object loadScript(String name) {19 return engine.loadScriptByName(name).getConstructor().newInstance()20 }2122}23
除非你在script name参数中传递相对路径,否则上述类无法加载位于给定文件夹的子目录中的脚本。这就是为什么最好这样写的原因:
import java.io.File;2import java.net.URL;3import groovy.util.GroovyScriptEngine;45public class ScriptManager {6 7 protected final File folder;8 protected final GroovyScriptEngine engine;910 public ScriptManager(File folder) {11 this.folder = folder;12 engine = createScriptEngine();13 }1415 protected GroovyScriptEngine createScriptEngine() {16 URL[] urls = { folder.toURI().toURL() };17 return new GroovyScriptEngine(urls, this.getClass().getClassLoader());18 }1920 public Object loadScript(File file) {21 String name = file.getAbsolutePath().substring(folder.getAbsolutePath().length() + 1);22 return engine.loadScriptByName(name).getConstructor().newInstance()23 }2425}
以上就是用Java NIO观看文件的方法介绍,详细使用情况还得要大家自己使用过才能知道具体要领。如果想阅读更多相关内容的文章,欢迎关注亿速云行业资讯频道!
声明:本站所有文章资源内容,如无特殊说明或标注,均为采集网络资源。如若本站内容侵犯了原著者的合法权益,可联系本站删除。