您好,登錄后才能下訂單哦!
這篇文章給大家分享的用Java NIO觀看文件的方法,相信大部分人都還沒學會這個技能,為了讓大家學會,給大家總結了以下內容,話不多說,一起往下看吧。
該 java.nio.file 包提供了一個文件更改通知API,被稱為 手表服務API。它使我們能夠在監視服務中注冊文件夾。注冊時,我們告訴服務我們感興趣的事件類型是:文件創建,文件修改或文件刪除。
當服務檢測到感興趣的事件時,會將其轉發到注冊的進程并根據需要進行處理。基本上是這樣的:
1.第一步是WatchService 使用類的newWatchService() 方法創建一個新 的 FileSystem。
2.接下來,我們使用Path 要關注的事件類型為要監視的文件夾注冊一個實例。
3.最后,我們實現了一個無限循環來等待傳入事件。當事件發生時,該密鑰會發出信號,并放入觀察者的隊列中。處理完事件后,我們需要ready 通過調用其reset() 方法將其放回 狀態 。如果返回false,則密鑰不再有效,并且循環可以退出。
4.1
5.WatchService watchService = FileSystems.getDefault().newWatchService();
6.2
7.Path path = Paths.get("c:\\directory");
8.3
9.path.register(watchService, ENTRY_CREATE, ENTRY_MODIFY, ENTRY_DELETE);
10.4
11.boolean poll = true;
12.5
13.while (poll) {
14.6
15. WatchKey key = watchService.take();
16.7
17. for (WatchEvent<?> event : key.pollEvents()) {
18.8
19. System.out.println("Event kind : " + event.kind() + " - File : " + event.context());
20.9
21. }
22.10
23. poll = key.reset();
24.11
25.}
這是控制臺輸出:
1
Event kind : ENTRY_CREATE - File : file.txt
2
Event kind : ENTRY_DELETE - File : file.txt
3
Event kind : ENTRY_CREATE - File : test.txt
4
Event kind : ENTRY_MODIFY - File : test.txt
WatchService API的級別很低,允許我們對其進行自定義。在本文中,按照Observer模式,我們將在此機制之上設計一個高級API,以偵聽給定文件夾的文件事件。我們將從創建一個FileEvent類開始,該類擴展了java.util.EventObject 所有事件狀態對象的來源。甲FileEvent 實例被構造為具有到源,這是邏輯上在其上事件發生在所述文件的引用。
FileEvent.java
import java.io.File;
2
import java.util.EventObject;
3
4
public class FileEvent extends EventObject {
5
6
public FileEvent(File file) {
7
super(file);
8
}
9
10
public File getFile() {
11
return (File) getSource();
12
}
13
14
}
接下來,我們創建一個FileListener必須由觀察者實現的接口,以便在發生文件事件時得到通知。它擴展了java.util.EventListener接口,這是所有事件偵聽器接口都必須擴展的標記接口。
FileListener.java
import java.util.EventListener;
2
3
public interface FileListener extends EventListener {
4
5
public void onCreated(FileEvent event);
6
7
public void onModified(FileEvent event);
8
9
public void onDeleted(FileEvent event);
10
11
}
難題的最后一步是創建主題,該主題維護觀察者列表,并通過調用其方法之一來通知狀態更改。我們將其命名FileWatcher并提供一個文件夾,這就是構造此類實例的方式。
1
public class FileWatcher {
2
3
protected List<FileListener> listeners = new ArrayList<>();
4
protected final File folder;
5
6
public FileWatcher(File folder) {
7
this.folder = folder;
8
}
9
10
public List<FileListener> getListeners() {
11
return listeners;
12
}
13
14
public FileWatcher setListeners(List<FileListener> listeners) {
15
this.listeners = listeners;
16
return this;
17
}
18
19
}
它可以實現該Runnable接口,因此,watch()如果文件夾存在,則在調用其方法時可以使用守護程序線程啟動監視進程 。
public class FileWatcher implements Runnable {
2
3
public void watch() {
4
if (folder.exists()) {
5
Thread thread = new Thread(this);
6
thread.setDaemon(true);
7
thread.start();
8
}
9
}
10
11
@Override
12
public void run() {
13
// implementation not yet provided
14
}
15
16
}
在其run()方法的實現中 ,將WatchService創建一個實例以輪詢try-with-resources語句中的事件。我們將使用類中的靜態最終列表來對其進行跟蹤FileWatcher,因此我們稍后可以調用其close()方法,以使任何等待檢索鍵的線程拋出未選中的對象ClosedWatchServiceException,這將以干凈的方式中斷監視過程。因此, 當正常關閉應用程序時,我們不會收到內存泄漏警告。
public class FileWatcher implements Runnable {
2
3
public void watch() {
4
if (folder.exists()) {
5
Thread thread = new Thread(this);
6
thread.setDaemon(true);
7
thread.start();
8
}
9
}
10
11
@Override
12
public void run() {
13
// implementation not yet provided
14
}
15
16
}
1
public class FileWatcher implements Runnable {
2
3
protected static final List<WatchService> watchServices = new ArrayList<>();
4
5
@Override
6
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
}
19
20
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
}
28
29
public static List<WatchService> getWatchServices() {
30
return Collections.unmodifiableList(watchServices);
31
}
32
33
}
每當發生事件時,文件路徑都會被解析,并相應地通知偵聽器。如果是創建新文件夾,FileWatcher則將創建另一個實例進行監視。
public class FileWatcher implements Runnable {
2
3
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 directory
11
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
}
25
26
}
這是FileWatcher該類的完整清單。
FileWatcher.java
import static java.nio.file.StandardWatchEventKinds.*;
2
import java.io.File;
3
import java.io.IOException;
4
import java.nio.file.ClosedWatchServiceException;
5
import java.nio.file.FileSystems;
6
import java.nio.file.Path;
7
import java.nio.file.Paths;
8
import java.nio.file.WatchEvent;
9
import java.nio.file.WatchKey;
10
import java.nio.file.WatchService;
11
import java.util.ArrayList;
12
import java.util.Collections;
13
import java.util.List;
14
15
public class FileWatcher implements Runnable {
16
17
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
}
24
25
public void watch() {
26
if (folder.exists()) {
27
Thread thread = new Thread(this);
28
thread.setDaemon(true);
29
thread.start();
30
}
31
}
32
33
@Override
34
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
}
47
48
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
}
56
57
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
}
78
79
public FileWatcher addListener(FileListener listener) {
80
listeners.add(listener);
81
return this;
82
}
83
84
public FileWatcher removeListener(FileListener listener) {
85
listeners.remove(listener);
86
return this;
87
}
88
89
public List<FileListener> getListeners() {
90
return listeners;
91
}
92
93
public FileWatcher setListeners(List<FileListener> listeners) {
94
this.listeners = listeners;
95
return this;
96
}
97
98
public static List<WatchService> getWatchServices() {
99
return Collections.unmodifiableList(watchServices);
100
}
101
102
}
我們設計的最后一點就是創建一個FileAdapter類,該類提供了FileListener 接口的默認實現,因此我們只能處理很少的事件來保存代碼。
FileAdapter.java
public abstract class FileAdapter implements FileListener {
2
3
@Override
4
public void onCreated(FileEvent event) {
5
// no implementation provided
6
}
7
8
@Override
9
public void onModified(FileEvent event) {
10
// no implementation provided
11
}
12
13
@Override
14
public void onDeleted(FileEvent event) {
15
// no implementation provided
16
}
17
18
}
該 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
}
17
18
protected void watch(File folder) {
19
new FileWatcher(folder).addListener(new FileAdapter() {
20
@Override
21
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
}
30
31
protected void process(File script) {
32
Object object = scriptManager.loadScript(script);
33
// update the application accordingly
34
}
盡管據說Thread.sleep() 在單元測試中使用該方法通常不是一個好主意,但FileWatcher由于需要在操作之間進行延遲,因此我們仍將使用它為該類編寫測試用例 。
import static org.junit.Assert.*;
2
import java.io.File;
3
import java.io.FileWriter;
4
import java.io.IOException;
5
import java.util.HashMap;
6
import java.util.Map;
7
import org.junit.Test;
8
9
public class FileWatcherTest {
10
11
@Test
12
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
}
40
41
public void wait(int time) throws InterruptedException {
42
Thread.sleep(time);
43
}
44
45
}
46
對于我來說,這可能是糾正其實現的絕好機會,Class.newInstance()方法是將不推薦使用的方法替換為該Class.getConstructor().newInstance()方法,以使其正確無誤。
import java.io.File;
2
import java.net.URL;
3
import groovy.util.GroovyScriptEngine;
4
5
public class ScriptManager {
6
7
protected final GroovyScriptEngine engine;
8
9
public ScriptManager(File folder) {
10
engine = createScriptEngine(folder);
11
}
12
13
protected GroovyScriptEngine createScriptEngine(File folder) {
14
URL[] urls = { folder.toURI().toURL() };
15
return new GroovyScriptEngine(urls, this.getClass().getClassLoader());
16
}
17
18
public Object loadScript(String name) {
19
return engine.loadScriptByName(name).getConstructor().newInstance()
20
}
21
22
}
23
除非你在script name參數中傳遞相對路徑,否則上述類無法加載位于給定文件夾的子目錄中的腳本。這就是為什么最好這樣寫的原因:
import java.io.File;
2
import java.net.URL;
3
import groovy.util.GroovyScriptEngine;
4
5
public class ScriptManager {
6
7
protected final File folder;
8
protected final GroovyScriptEngine engine;
9
10
public ScriptManager(File folder) {
11
this.folder = folder;
12
engine = createScriptEngine();
13
}
14
15
protected GroovyScriptEngine createScriptEngine() {
16
URL[] urls = { folder.toURI().toURL() };
17
return new GroovyScriptEngine(urls, this.getClass().getClassLoader());
18
}
19
20
public Object loadScript(File file) {
21
String name = file.getAbsolutePath().substring(folder.getAbsolutePath().length() + 1);
22
return engine.loadScriptByName(name).getConstructor().newInstance()
23
}
24
25
}
以上就是用Java NIO觀看文件的方法介紹,詳細使用情況還得要大家自己使用過才能知道具體要領。如果想閱讀更多相關內容的文章,歡迎關注億速云行業資訊頻道!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。