Introduction
Khi các nhóm pháp lý hoặc các nhà phân tích pháp y cần chứng minh rằng một tài liệu không bị thay đổi, chỉ nhìn vào nội dung hiển thị là không đủ. Các thuộc tính ẩn — chẳng hạn như tác giả, ngày tạo, hoặc số phiên bản — có thể tiết lộ ai đã chạm vào tệp và khi nào. Việc phát hiện những thay đổi tinh vi này qua các phiên bản tài liệu là một điểm đau phổ biến, thường đòi hỏi phải kiểm tra thủ công từng thuộc tính, một công việc tốn thời gian và dễ gây lỗi.
GroupDocs.Metadata cho Java cung cấp cách lập trình để trích xuất mọi trường metadata và tính toán sự khác biệt có cấu trúc giữa hai phiên bản. Trong hướng dẫn này chúng ta sẽ so sánh ba cách tiếp cận thực tiễn: so sánh toàn bộ metadata, phát hiện thay đổi quyền sở hữu, và phân tích lịch sử phiên bản. Mỗi phương pháp được minh họa bằng đoạn mã ngắn gọn, có thể sao chép và dán, và chúng ta cũng sẽ chỉ cách xuất kết quả ra CSV hoặc JSON để báo cáo kiểm toán.
Tôi gặp vấn đề này khi xem xét một hợp đồng đã được chỉnh sửa bởi nhiều bên trong nhiều tháng; nội dung hiển thị trông giống hệt nhau, nhưng các trường quyền sở hữu đã thay đổi một cách âm thầm.
How can I tell which metadata fields changed between two document versions?
GroupDocs.Metadata tải mỗi tệp, trích xuất tất cả các thuộc tính có thể truy cập vào một map, sau đó lặp qua các khóa để phân loại các mục được thêm, bị xóa và thay đổi. Thư viện xử lý cả các thẻ tích hợp sẵn và các thẻ tùy chỉnh, vì vậy bạn nhận được một bức tranh toàn diện mà không cần viết trình phân tích riêng cho từng định dạng. Kết quả là một đối tượng MetadataDiff mà bạn có thể truy vấn hoặc tuần tự hoá để tạo báo cáo tuân thủ.
Prerequisites
- Java 8 trở lên
- GroupDocs.Metadata cho Java 24.7 ([giấy phép tạm thời][TEMP_LICENSE_URL])
- Hai tệp tài liệu bạn muốn so sánh (ví dụ:
contract_v1.pdfvàcontract_v2.pdf)
Installation
Thêm dependency qua Maven:
<dependency>
<groupId>com.groupdocs</groupId>
<artifactId>groupdocs-metadata</artifactId>
<version>24.7</version>
</dependency>
Method 1 – Full Metadata Diff
Phương pháp này trích xuất tất cả thuộc tính metadata từ cả hai phiên bản và báo cáo các mục được thêm, bị xóa và thay đổi.
// CompareMetadataSets.run – returns a MetadataDiff object
Map<String, String> v1 = ExtractAllMetadata.run(pathV1);
Map<String, String> v2 = ExtractAllMetadata.run(pathV2);
MetadataDiff diff = new MetadataDiff();
// Detect added and changed properties
for (Map.Entry<String, String> e : v2.entrySet()) {
String key = e.getKey();
String val = e.getValue();
if (!v1.containsKey(key)) {
diff.added.put(key, val); // New property in v2
} else if (!v1.get(key).equals(val)) {
diff.changed.put(key, new String[]{v1.get(key), val}); // Value changed
}
}
// Detect removed properties
for (Map.Entry<String, String> e : v1.entrySet()) {
if (!v2.containsKey(e.getKey())) {
diff.removed.put(e.getKey(), e.getValue());
}
}
return diff;
Các điểm chính:
- Toàn diện: Nắm bắt mọi thẻ, kể cả thẻ tùy chỉnh.
- Logic map đơn giản: Không cần thư viện diff bên ngoài.
- Đối tượng kết quả: Các map
added,removed, vàchangedđã sẵn sàng để xử lý tiếp.
💡 Mẹo: Dùng phương pháp này khi bạn cần một chuỗi kiểm toán đầy đủ cho mục đích tuân thủ quy định.
Method 2 – Detect Ownership Changes
Các tranh chấp pháp lý thường xoay quanh việc ai đã tạo hoặc chỉnh sửa tài liệu. Phương pháp này tập trung vào các thẻ liên quan đến con người như Creator, Editor, Manager và Company.
// DetectOwnershipChanges.run – returns a map of changed ownership fields
Map<String, String> v1 = readOwnership(pathV1);
Map<String, String> v2 = readOwnership(pathV2);
Set<String> allKeys = new HashSet<>(v1.keySet());
allKeys.addAll(v2.keySet());
Map<String, String[]> changes = new LinkedHashMap<>();
for (String key : allKeys) {
String oldVal = v1.getOrDefault(key, "<missing>");
String newVal = v2.getOrDefault(key, "<missing>");
if (!oldVal.equals(newVal)) {
changes.put(key, new String[]{oldVal, newVal});
}
}
return changes;
readOwnership chỉ lấy các thẻ liên quan:
Map<String, String> result = new LinkedHashMap<>();
try (Metadata metadata = new Metadata(path)) {
if (metadata.getFileFormat() == FileFormat.Unknown) return result;
for (MetadataProperty p : metadata.findProperties(
new ContainsTagSpecification(Tags.getPerson().getCreator())
.or(new ContainsTagSpecification(Tags.getPerson().getEditor()))
.or(new ContainsTagSpecification(Tags.getPerson().getManager()))
.or(new ContainsTagSpecification(Tags.getCorporate().getCompany())))) {
String value = "";
if (p.getValue() != null && p.getValue().getRawValue() != null) {
value = String.valueOf(p.getValue().getRawValue());
}
result.put(p.getName(), value);
}
}
return result;
Các điểm chính:
- Mục tiêu: Chỉ kiểm tra các thuộc tính mang tính nhận dạng.
- Đầu ra rõ ràng: Trả về một map, mỗi mục hiển thị giá trị
[cũ, mới]. - Sẵn sàng cho tuân thủ: Hoàn hảo cho e‑discovery hoặc tranh chấp quyền sở hữu hợp đồng.
💡 Mẹo: Kết hợp phương pháp này với diff toàn bộ nếu bạn cần cả chiều rộng và chiều sâu.
Method 3 – Detect Revision History Changes
Số phiên bản, thời gian chỉnh sửa và ngày in là những thông tin ẩn với người dùng cuối nhưng lại quan trọng cho dòng thời gian pháp y