Java: Quan sát sự thay đổi thư mục

Các khóa học qua video:
Python SQL Server PHP C# Lập trình C Java HTML5-CSS3-JavaScript
Học trên YouTube <76K/tháng. Đăng ký Hội viên
Viết nhanh hơn - Học tốt hơn
Giải phóng thời gian, khai phóng năng lực

Đã bao giờ bạn thấy mình chỉnh sửa một tập tin, sử dụng một IDE hoặc soạn thảo khác, một hộp thoại hiện ra để thông báo cho bạn rằng một trong những tập tin mở đã thay đổi trên hệ thống tập tin và cần phải được tải lại? Hoặc có lẽ, như NetBeans IDE, ứng dụng chỉ lặng lẽ cập nhật các tập tin mà không thông báo cho bạn. Hộp thoại mẫu sau đây cho thấy cách thông báo của trình biên tập miễn phí là jEdit:

Mẫu jEdit Dialog nêu rõ: Các tập tin sau đây đã được thay đổi trên đĩa bởi một chương trình khác.

Hộp thoại jEdit hiển thị rằng một tập tin thay đổi đã được nhận diện

Để thực hiện chức năng này, gọi là thông báo thay đổi tập tin, thì một chương trình phải có khả năng phát hiện những gì đang xảy ra với các thư mục có liên quan về các hệ thống tập tin. Một cách để làm điều đó là thăm dò các tập tin hệ thống để tìm kiếm sự thay đổi, nhưng phương pháp này là không hiệu quả. Nó không phù hợp với các ứng dụng mà có hàng trăm các tập tin mở hoặc thư mục để giám sát.

Gói java.nio.file cung cấp một API thông báo thay đổi tập tin, được gọi là API Dịch vụ Xem. API này cho phép bạn đăng ký một thư mục với dịch vụ xem. Khi đăng ký, bạn sẽ báo lại cho dịch vụ loại sự kiện mà bạn đang quan tâm: tạo tập tin, xóa tập tin, hoặc sửa đổi tập tin. Khi dịch vụ phát hiện một sự kiện bạn quan tâm, nó được chuyển tiếp đến quá trình đăng ký. Quá trình đăng ký có một luồng dành riêng để xem cho bất kỳ sự kiện đã đăng ký nào. Khi một sự kiện xuất hiện thì nó sẽ được xử lý khi cần thiết.

Bài viết này trình bày những vấn đề sau đây:

  • Tổng quan Dịch vụ Xem
  • Thử nghiệm
  • Tạo một dịch vụ xem và đăng ký sự kiện
  • Xử lý sự kiện
  • Lấy tên tập tin
  • Khi nào nên và không nên sử dụng API này

Tổng quan Dịch vụ Xem

API WatchService là mức khá thấp, nó cho phép bạn tùy chỉnh nó. Bạn có thể sử dụng nó để tạo ra một API cấp cao ở mức top của của cơ chế này để nó phù hợp với nhu cầu cụ thể của bạn.

Dưới đây là các bước cơ bản cần thiết để thực hiện một dịch vụ xem:

  • Tạo một WatchService làm "bộ xem" cho hệ thống tập tin.
  • Đối với mỗi thư mục mà bạn muốn được giám sát, hãy đăng ký nó với bộ xem. Khi đăng ký một thư mục, bạn chỉ định kiểu của các sự kiện mà bạn muốn thông báo. Bạn nhận được một thể hiện WatchKey cho mỗi thư mục mà bạn đăng ký.
  • Thực hiện một vòng lặp vô hạn để chờ đợi cho các sự kiện sắp tới. Khi một sự kiện xảy ra, nó sẽ báo hiệu và được đặt vào hàng đợi của bộ xem.
  • Lấy khóa từ hàng đợi của bộ xem. Bạn có thể có được tên tập tin khóa.
  • Lấy mỗi sự kiện cấp phát cho khóa (có thể có nhiều sự kiện) và xử lý khi cần thiết.
  • Đặt lại khóa, và tiếp tục chờ đợi cho các sự kiện.
  • Đóng dịch vụ: Dịch vụ xem sẽ thoát ra khi hoặc là luồng sẽ thoát hoặc khi nó được đóng lại (bằng cách gọi phương thức closed của nó).

WatchKeys là luồng an toàn và có thể được sử dụng với gói java.nio.concurrent. Bạn có thể sử dụng một thread pool để làm điều này.

Thử nghiệm

Do API này cao cấp hơn, bạn hãy thử nghiệm nó ra trước khi tiếp tục. Lưu ví dụ WatchDir vào máy tính của bạn, và biên dịch nó. Tạo một thư mục có tên test để truyền đến ví dụ. WatchDir sử dụng một luồng duy nhất để xử lý tất cả các sự kiện, vì vậy nó sẽ chặn dữ liệu đầu vào từ bàn phím trong khi chờ đợi sự kiện. Hoặc chạy các chương trình trong một cửa sổ riêng biệt, hoặc ở chế độ nền, như sau:

java WatchDir test &

Thực hiện việc tạo, xóa, và chỉnh sửa các tập tin trong thư mục test. Khi một trong những sự kiện này xảy ra, một thông điệp được in vào giao diện điều khiển. Khi bạn đã hoàn thành, xóa thư mục test và thoát khỏi WatchDir, hoặc nếu bạn muốn bạn có thể tự hủy quá trình xử lý.

Bạn cũng có thể xem toàn bộ cây tập tin bằng cách chỉ định tùy chọn -r. Khi bạn chỉ định -rWatchDir sẽ đi bộ qua cây tập tin, đăng ký mỗi thư mục với dịch vụ xem.

Tạo một dịch vụ xem và đăng ký sự kiện

Bước đầu tiên là tạo ra một WatchService mới bằng phương thức newWatchService trong lớp FileSystem như sau:

WatchService watcher = FileSystems.getDefault().newWatchService();

Tiếp theo, bạn đăng ký một hoặc nhiều đối tượng với dịch vụ xem. Bất kỳ đối tượng nào thực thi giao diện watchable đều có thể được đăng ký, lớp Path sẽ thực thi giao diện watchable, vì vậy mỗi thư mục đã được xét đến thì đều được đăng ký như là một đối tượng Path.

Với bất kỳ một Watchable nào thì lớp Path sẽ thực hiện hai phương thức register. Trong bài viết này sử dụng phương thức register đối số là register(WatchService, WatchEvent.Kind<?>...), đối với phương thức register ba ​​đối số thì đối số thứ ba là môt có một WatchEvent.Modifier , mà hiện không được thực hiện.)

Khi đăng ký một đối tượng với dịch vụ xem, bạn chỉ định loại sự kiện mà bạn muốn theo dõi. Sự kiện StandardWatchEventKinds hỗ trợ các loại sau:

  • ENTRY_CREATE - Một thư mục đầu vào được tạo ra.
  • ENTRY_DELETE - Một thư mục đầu vào bị xóa.
  • ENTRY_MODIFY - Một thư mục đầu vào được sửa đổi.
  • OVERFLOW - Chỉ ra các sự kiện có thể đã bị mất hoặc bị loại bỏ. Bạn không cần phải đăng ký sự kiện OVERFLOW sự kiện để nhận được nó.

Đoạn mã sau đây cho thấy cách để đăng ký một thể hiện Path cho cả ba loại sự kiện trên:

import static java.nio.file.StandardWatchEventKinds.*;

Path dir = ...;
try {
    WatchKey key = dir.register(watcher,
                           ENTRY_CREATE,
                           ENTRY_DELETE,
                           ENTRY_MODIFY);
} catch (IOException x) {
    System.err.println(x);
}

Xử lý sự kiện

Trình tự của các sự kiện trong một vòng lặp xử lý sự kiện như sau:

  1. Nhận khóa xem. Ba phương thức sau được cung cấp:
  • poll - Trả về một khóa hàng đợi nếu có. Trả lại ngay lập tức null nếu không có sẵn.
  • poll(long, TIMEUNIT) - Trả về một khóa hàng đợi nếu có sẵn. Nếu một khóa hàng đợi là không có tức thì thì chương trình sẽ đợi cho đến khi thời gian được xác định. Đối số TIMEUNIT xác định thời gian quy định là nano giây, mili giây, hoặc một số đơn vị khác.
  • take - Trả về một khóa hàng đợi. Nếu không có sẵn thì phương thức này sẽ chờ.
  1. Xử lý các sự kiện cấp phát cho khóa. Bạn sẽ lấy một danh sách các WatchEvents từ phương thức pollEvents.
  2. Here>>Lấy kiểu của sự kiện bằng cách sử dụng các phương thức take. Không có vấn đề gì các sự kiện phím đã đăng ký, nó có thể nhận được một OVERFLOW sự kiện. Bạn có thể chọn để xử lý tràn hoặc bỏ qua nó, nhưng bạn nên kiểm tra cho nó.
  3. Lấy tên tập tin liên quan đến sự kiện này. Các tên tập tin được lưu trữ như là bối cảnh của sự kiện, vì vậy các bối cảnhphương pháp được sử dụng để lấy nó.
  4. Sau khi các sự kiện cho chìa khóa đã được xử lý, bạn cần phải đặt phím trở thành một chuẩn nhà nước bằng cách gọithiết lập lại . Nếu phương pháp này trả về sai , chính là không còn giá trị và các vòng lặp có thể thoát ra. Bước này rất quan trọng . Nếu bạn thất bại để gọi thiết lập lại , phím này sẽ không nhận được bất kỳ sự kiện hơn nữa.

Một khóa đồng hồ có một trạng thái. Tại bất kỳ thời điểm nào, trạng thái của nó có thể là một trong những cách sau:

  • Sẵn sàng chỉ ra rằng chính là sẵn sàng chấp nhận sự kiện. Khi lần đầu tiên được tạo ra, một chìa khóa ở trạng thái sẵn sàng.
  • Hiệu chỉ ra rằng một hoặc nhiều sự kiện được xếp hàng đợi. Một khi các trọng điểm đã được báo hiệu, nó không còn ở trạng thái sẵn sàng cho đến khi thiết lập lại phương pháp được gọi.
  • Không hợp lệ chỉ ra rằng chính không còn hoạt động. Trạng thái này xảy ra khi một trong các sự kiện sau đây xảy ra:
    • Quá trình hủy bỏ một cách rõ ràng là chìa khóa bằng hủy bỏ phương pháp.
    • Các thư mục trở nên không thể tiếp cận.
    • Các dịch vụ hồ được đóng lại .

Dưới đây là một ví dụ của một vòng lặp xử lý sự kiện. Nó được lấy từ Email ví dụ, mà đồng hồ một thư mục, chờ đợi cho các tập tin mới xuất hiện. Khi một tập tin mới trở nên có sẵn, nó được kiểm tra để xác định xem nó là một văn bản / đồng bằng tập tin bằng cách sử dụng các probeContentType (Path) phương pháp. Mục đích là text / plain tập tin sẽ được gửi đến một bí danh, nhưng mà chi tiết thực hiện được để lại cho người đọc.

Các phương pháp cụ thể cho các API dịch vụ đồng hồ được in đậm:

for (;;) {

    // chờ đến khi key được gán
    WatchKey key;
    try {key = watcher.take();
    } catch (InterruptedException x) {
        return;
    }

    for (WatchEvent<?> event: key.pollEvents()) {
        WatchEvent.Kind<?> kind = event.kind();

        // key này chỉ được gán cho sự kiện ENTRY_CREATE events,
        // but an OVERFLOW event can
        // occur regardless if events
        // are lost or discarded.
        if (kind == OVERFLOW) {
            continue;
        }

        // The filename is the
        // context of the event.
        WatchEvent<Path> ev = (WatchEvent<Path>)event;
        Path filename = ev.context();

        // Verify that the new
        //  file is a text file.
        try {
            // Resolve the filename against the directory.
            // If the filename is "test" and the directory is "foo",
            // the resolved name is "test/foo".
            Path child = dir.resolve(filename);
            if (!Files.probeContentType(child).equals("text/plain")) {
                System.err.format("New file '%s'" +
                    " is not a plain text file.%n", filename);
                continue;
            }
        } catch (IOException x) {
            System.err.println(x);
            continue;
        }

        // Email the file to the
        //  specified email alias.
        System.out.format("Emailing file %s%n", filename);
        //Details left to reader....
    }

    // Reset the key -- this step is critical if you want to
    // receive further watch events.  If the key is no longer valid,
    // the directory is inaccessible so exit the loop.
    boolean valid = key.reset();
    if (!valid) {
        break;
    }
}

Lấy tên tập tin

Các tên tập tin được lấy từ bối cảnh sự kiện. Các Email dụ lấy tên tập tin với mã này:

WatchEvent <Đường dẫn> ev = (WatchEvent <Đường dẫn>) sự kiện; 
Đường dẫn filename = ev.context ();

Khi bạn biên dịch Email dụ, nó tạo ra các lỗi sau đây:

Lưu ý:. Email.java sử dụng hoạt động đánh dấu hoặc không an toàn 
Lưu ý: Biên dịch lại với -Xlint: bỏ chọn để biết chi tiết.

Lỗi này là kết quả của các dòng mã mà phôi WatchEvent <T> để một WatchEvent <Đường dẫn> . Các WatchDir dụ tránh lỗi này bằng cách tạo ra một tiện ích cast phương pháp ngăn chặn cảnh báo không được kiểm soát, như sau:

@SuppressWarnings ( "Kiểm soát") 
<T> WatchEvent <T> đúc (<?> WatchEvent sự kiện) {tĩnh 
    trở lại (WatchEvent <Đường dẫn>) sự kiện; 
}

Nếu bạn không quen với các @SuppressWarnings cú pháp, xem chú thích .

Khi nào nên và không nên sử dụng API này

The Watch Service API được thiết kế cho các ứng dụng cần phải được thông báo về các sự kiện thay đổi tập tin. Nó rất thích hợp cho bất kỳ ứng dụng, như một biên tập viên hay IDE, mà có khả năng có nhiều tập tin mở và cần phải đảm bảo rằng các tập tin được đồng bộ với hệ thống tập tin. Nó cũng rất thích hợp cho một máy chủ ứng dụng đồng hồ một thư mục, có lẽ chờ đợi .jsphoặc .jar file để thả, để triển khai chúng.

API này không được thiết kế để lập chỉ mục một ổ đĩa cứng. Hầu hết các triển khai hệ thống tập tin có hỗ trợ cho thông báo thay đổi tập tin. Các API Xem Dịch vụ tận dụng sự hỗ trợ này nếu có. Tuy nhiên, khi một hệ thống tập tin không hỗ trợ cơ chế này, các dịch vụ Watch sẽ thăm dò ý kiến các hệ thống tập tin, chờ đợi cho các sự kiện.

» Tiếp: Các phương thức hữu dụng khác
« Trước: Tìm tập tin
Các khóa học qua video:
Python SQL Server PHP C# Lập trình C Java HTML5-CSS3-JavaScript
Học trên YouTube <76K/tháng. Đăng ký Hội viên
Viết nhanh hơn - Học tốt hơn
Giải phóng thời gian, khai phóng năng lực
Copied !!!