给项目做一个目录监控功能
系统启动后想给系统添加一个项目监控功能, 如果有新增文件,就读文件做相应的业务
import java.nio.file.*;
import static java.nio.file.StandardWatchEventKinds.*;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.concurrent.atomic.AtomicBoolean;
public class FileMonitor implements AutoCloseable {
private final WatchService watchService;
private final Path monitorPath;
private final AtomicBoolean isRunning;
private static final DateTimeFormatter TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
private static final int MAX_DISPLAY_CONTENT_LENGTH = 2000;
private static final long MAX_FILE_SIZE = 1024 * 1024; // 1MB
public FileMonitor(String directoryPath) throws IOException {
this.monitorPath = Paths.get(directoryPath);
this.watchService = FileSystems.getDefault().newWatchService();
this.isRunning = new AtomicBoolean(true);
ensureDirectoryExists();
registerWatchService();
}
private void ensureDirectoryExists() throws IOException {
if (!Files.exists(monitorPath)) {
Files.createDirectories(monitorPath);
System.out.println("目录不存在,已创建: " + monitorPath.toAbsolutePath());
}
if (!Files.isDirectory(monitorPath)) {
throw new IllegalArgumentException("路径不是目录: " + monitorPath);
}
}
/**
* 注册监听事件, 注册后事件都会被watchService拿到, 只有通过poll,take才能拿到具体事件
*/
private void registerWatchService() throws IOException {
monitorPath.register(watchService, ENTRY_CREATE);
}
public void startMonitoring() {
System.out.println("开始监控目录: " + monitorPath.toAbsolutePath());
System.out.println("等待新文件创建... (按 Ctrl+C 退出)");
try {
while (isRunning.get() && !Thread.currentThread().isInterrupted()) {
processWatchEvents();
}
} catch (ClosedWatchServiceException e) {
System.out.println("监控服务已正常关闭");
}
}
private void processWatchEvents() {
try {
WatchKey key = watchService.poll(1, java.util.concurrent.TimeUnit.SECONDS);
if (key == null) {
return; // 超时,继续检查运行状态
}
processKeyEvents(key);
resetWatchKey(key);
} catch (InterruptedException e) {
System.out.println("监控被中断,正在退出...");
Thread.currentThread().interrupt();
isRunning.set(false);
}
}
private void processKeyEvents(WatchKey key) {
key.pollEvents().stream()
.filter(event -> event.kind() != OVERFLOW)
.filter(event -> event.kind() == ENTRY_CREATE)
.forEach(this::handleCreateEvent);
}
private void handleCreateEvent(WatchEvent<?> event) {
WatchEvent<Path> pathEvent = (WatchEvent<Path>) event;
Path fileName = pathEvent.context();
Path fullPath = monitorPath.resolve(fileName);
waitForFileStabilization();
if (Files.isRegularFile(fullPath)) {
processNewFile(fullPath);
}
}
private void waitForFileStabilization() {
try {
Thread.sleep(100); // 等待文件稳定
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
private void processNewFile(Path filePath) {
try {
FileInfo fileInfo = collectFileInfo(filePath);
displayFileInfo(fileInfo);
} catch (Exception e) {
System.err.printf("处理文件 %s 时出错: %s%n", filePath.getFileName(), e.getMessage());
}
}
private FileInfo collectFileInfo(Path filePath) throws IOException {
LocalDateTime createTime = getFileCreationTime(filePath);
String content = readFileContentSafely(filePath);
long fileSize = Files.size(filePath);
return new FileInfo(
filePath.getFileName().toString(),
filePath.toAbsolutePath().toString(),
createTime,
fileSize,
content
);
}
private LocalDateTime getFileCreationTime(Path filePath) {
try {
return LocalDateTime.ofInstant(
Files.getLastModifiedTime(filePath).toInstant(),
java.time.ZoneId.systemDefault()
);
} catch (IOException e) {
System.out.println("无法获取文件时间,使用当前时间");
return LocalDateTime.now();
}
}
private String readFileContentSafely(Path filePath) {
try {
if (Files.size(filePath) > MAX_FILE_SIZE) {
return readLimitedContent(filePath);
}
return Files.readString(filePath, StandardCharsets.UTF_8);
} catch (IOException e) {
return String.format("[无法读取文件内容: %s]", e.getMessage());
}
}
private String readLimitedContent(Path filePath) throws IOException {
return "[文件过大,仅显示部分内容]\n" +
Files.lines(filePath, StandardCharsets.UTF_8)
.limit(10)
.reduce("", (a, b) -> a + b + "\n");
}
private void displayFileInfo(FileInfo fileInfo) {
System.out.println("\n" + "=".repeat(80));
System.out.println("检测到新文件!");
System.out.printf("文件名称: %s%n", fileInfo.name());
System.out.printf("完整路径: %s%n", fileInfo.fullPath());
System.out.printf("创建时间: %s%n", fileInfo.createTime().format(TIME_FORMATTER));
System.out.printf("文件大小: %s%n", formatFileSize(fileInfo.size()));
System.out.println("-".repeat(40));
System.out.println("文件内容:");
System.out.println("-".repeat(40));
String displayContent = fileInfo.content().length() > MAX_DISPLAY_CONTENT_LENGTH ?
fileInfo.content().substring(0, MAX_DISPLAY_CONTENT_LENGTH) + "\n... [内容过长,已截断]" :
fileInfo.content();
System.out.println(displayContent);
System.out.println("=".repeat(80) + "\n");
}
private String formatFileSize(long bytes) {
if (bytes < 1024) return bytes + " B";
if (bytes < 1024 * 1024) return String.format("%.2f KB", bytes / 1024.0);
return String.format("%.2f MB", bytes / (1024.0 * 1024.0));
}
private void resetWatchKey(WatchKey key) {
if (!key.reset()) {
System.out.println("监控Key无效,退出监控");
isRunning.set(false);
}
}
public void stop() {
isRunning.set(false);
}
@Override
public void close() {
if (watchService != null) {
try {
watchService.close();
System.out.println("监控服务已关闭");
} catch (IOException e) {
System.err.println("关闭监控服务时出错: " + e.getMessage());
}
}
}
public static void main(String[] args) {
String directoryPath = "C:\\Users\\Rzxuser\\Desktop\\dbbac";
try (FileMonitor monitor = new FileMonitor(directoryPath)) {
setupShutdownHook(monitor);
monitor.startMonitoring();
} catch (IOException e) {
System.err.println("初始化监控服务失败: " + e.getMessage());
e.printStackTrace();
}
}
private static void setupShutdownHook(FileMonitor monitor) {
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
System.out.println("\n接收到关闭信号,正在清理资源...");
monitor.stop();
}));
}
// 记录类封装文件信息
private record FileInfo(
String name,
String fullPath,
LocalDateTime createTime,
long size,
String content
) {}
}
实际使用可以通过文件名.ok文件来判断是否有写完整. 写完整后读对应的文件写入kafka, 然后删掉文件.
import java.nio.file.*;
import static java.nio.file.StandardWatchEventKinds.*;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.concurrent.atomic.AtomicBoolean;
public class FileMonitor implements AutoCloseable {
private final WatchService watchService;
private final Path monitorPath;
private final AtomicBoolean isRunning;
private static final DateTimeFormatter TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
private static final int MAX_DISPLAY_CONTENT_LENGTH = 2000;
private static final long MAX_FILE_SIZE = 1024 * 1024; // 1MB
public FileMonitor(String directoryPath) throws IOException {
this.monitorPath = Paths.get(directoryPath);
this.watchService = FileSystems.getDefault().newWatchService();
this.isRunning = new AtomicBoolean(true);
ensureDirectoryExists();
registerWatchService();
}
private void ensureDirectoryExists() throws IOException {
if (!Files.exists(monitorPath)) {
Files.createDirectories(monitorPath);
System.out.println("目录不存在,已创建: " + monitorPath.toAbsolutePath());
}
if (!Files.isDirectory(monitorPath)) {
throw new IllegalArgumentException("路径不是目录: " + monitorPath);
}
}
/**
* 注册监听事件, 注册后事件都会被watchService拿到, 只有通过poll,take才能拿到具体事件
*/
private void registerWatchService() throws IOException {
monitorPath.register(watchService, ENTRY_CREATE);
}
public void startMonitoring() {
System.out.println("开始监控目录: " + monitorPath.toAbsolutePath());
System.out.println("等待新文件创建... (按 Ctrl+C 退出)");
try {
while (isRunning.get() && !Thread.currentThread().isInterrupted()) {
processWatchEvents();
}
} catch (ClosedWatchServiceException e) {
System.out.println("监控服务已正常关闭");
}
}
private void processWatchEvents() {
try {
WatchKey key = watchService.poll(1, java.util.concurrent.TimeUnit.SECONDS);
if (key == null) {
return; // 超时,继续检查运行状态
}
processKeyEvents(key);
resetWatchKey(key);
} catch (InterruptedException e) {
System.out.println("监控被中断,正在退出...");
Thread.currentThread().interrupt();
isRunning.set(false);
}
}
private void processKeyEvents(WatchKey key) {
key.pollEvents().stream()
.filter(event -> event.kind() != OVERFLOW)
.filter(event -> event.kind() == ENTRY_CREATE)
.forEach(this::handleCreateEvent);
}
private void handleCreateEvent(WatchEvent<?> event) {
WatchEvent<Path> pathEvent = (WatchEvent<Path>) event;
Path fileName = pathEvent.context();
Path fullPath = monitorPath.resolve(fileName);
waitForFileStabilization();
if (Files.isRegularFile(fullPath)) {
processNewFile(fullPath);
}
}
private void waitForFileStabilization() {
try {
Thread.sleep(100); // 等待文件稳定
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
private void processNewFile(Path filePath) {
try {
FileInfo fileInfo = collectFileInfo(filePath);
displayFileInfo(fileInfo);
} catch (Exception e) {
System.err.printf("处理文件 %s 时出错: %s%n", filePath.getFileName(), e.getMessage());
}
}
private FileInfo collectFileInfo(Path filePath) throws IOException {
LocalDateTime createTime = getFileCreationTime(filePath);
String content = readFileContentSafely(filePath);
long fileSize = Files.size(filePath);
return new FileInfo(
filePath.getFileName().toString(),
filePath.toAbsolutePath().toString(),
createTime,
fileSize,
content
);
}
private LocalDateTime getFileCreationTime(Path filePath) {
try {
return LocalDateTime.ofInstant(
Files.getLastModifiedTime(filePath).toInstant(),
java.time.ZoneId.systemDefault()
);
} catch (IOException e) {
System.out.println("无法获取文件时间,使用当前时间");
return LocalDateTime.now();
}
}
private String readFileContentSafely(Path filePath) {
try {
if (Files.size(filePath) > MAX_FILE_SIZE) {
return readLimitedContent(filePath);
}
return Files.readString(filePath, StandardCharsets.UTF_8);
} catch (IOException e) {
return String.format("[无法读取文件内容: %s]", e.getMessage());
}
}
private String readLimitedContent(Path filePath) throws IOException {
return "[文件过大,仅显示部分内容]\n" +
Files.lines(filePath, StandardCharsets.UTF_8)
.limit(10)
.reduce("", (a, b) -> a + b + "\n");
}
private void displayFileInfo(FileInfo fileInfo) {
System.out.println("\n" + "=".repeat(80));
System.out.println("检测到新文件!");
System.out.printf("文件名称: %s%n", fileInfo.name());
System.out.printf("完整路径: %s%n", fileInfo.fullPath());
System.out.printf("创建时间: %s%n", fileInfo.createTime().format(TIME_FORMATTER));
System.out.printf("文件大小: %s%n", formatFileSize(fileInfo.size()));
System.out.println("-".repeat(40));
System.out.println("文件内容:");
System.out.println("-".repeat(40));
String displayContent = fileInfo.content().length() > MAX_DISPLAY_CONTENT_LENGTH ?
fileInfo.content().substring(0, MAX_DISPLAY_CONTENT_LENGTH) + "\n... [内容过长,已截断]" :
fileInfo.content();
System.out.println(displayContent);
System.out.println("=".repeat(80) + "\n");
}
private String formatFileSize(long bytes) {
if (bytes < 1024) return bytes + " B";
if (bytes < 1024 * 1024) return String.format("%.2f KB", bytes / 1024.0);
return String.format("%.2f MB", bytes / (1024.0 * 1024.0));
}
private void resetWatchKey(WatchKey key) {
if (!key.reset()) {
System.out.println("监控Key无效,退出监控");
isRunning.set(false);
}
}
public void stop() {
isRunning.set(false);
}
@Override
public void close() {
if (watchService != null) {
try {
watchService.close();
System.out.println("监控服务已关闭");
} catch (IOException e) {
System.err.println("关闭监控服务时出错: " + e.getMessage());
}
}
}
public static void main(String[] args) {
String directoryPath = "C:\\Users\\Rzxuser\\Desktop\\dbbac";
try (FileMonitor monitor = new FileMonitor(directoryPath)) {
setupShutdownHook(monitor);
monitor.startMonitoring();
} catch (IOException e) {
System.err.println("初始化监控服务失败: " + e.getMessage());
e.printStackTrace();
}
}
private static void setupShutdownHook(FileMonitor monitor) {
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
System.out.println("\n接收到关闭信号,正在清理资源...");
monitor.stop();
}));
}
// 记录类封装文件信息
private record FileInfo(
String name,
String fullPath,
LocalDateTime createTime,
long size,
String content
) {}
}
无聊我就学英语

浙公网安备 33010602011771号