给项目做一个目录监控功能

系统启动后想给系统添加一个项目监控功能, 如果有新增文件,就读文件做相应的业务
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
) {}
}
posted @ 2025-10-24 16:00  trump2  阅读(5)  评论(0)    收藏  举报