💡 Plně funkční příklad je k dispozici na GitHubu:
metadata-diff-doc-versions-using-groupdocs-metadata-nodejs
Úvod
Porovnání metadat je schopnost GroupDocs.Metadata, která odhaluje skryté změny mezi verzemi dokumentů a umožňuje auditorům rychle ověřit pravost. Když právní týmy potřebují prokázat, že smlouva nebyla pozměněna, neviditelné vlastnosti — autor, editor, časové razítka revizí — často vypráví pravý příběh.
Na tento problém jsem narazil při revizi šarže smluv s dodavateli; každý soubor uváděl stejné datum vytvoření, ale skryté pole autora se lišilo a ruční audit by trval hodiny.
Proč jsou skrytá metadata důležitá pro právní týmy
Právní a compliance profesionálové se spoléhají na neměnný důkaz. I když viditelný text zůstane stejný, změna pole Creator nebo LastPrinted může naznačovat neautorizované úpravy. Včasné odhalení těchto posunů zabraňuje nákladným sporům a splňuje požadavky na auditní stopy.
Naše řešení s GroupDocs.Metadata
GroupDocs.Metadata pro Node.js poskytuje jednotné API, které extrahuje každou podporovanou vlastnost, porovná dvě revize a vytvoří strukturované diff‑zprávy. SDK podporuje DOCX, PDF, XLSX a mnoho dalších formátů „out‑of‑the‑box“, takže není potřeba psát vlastní parsery pro každý typ souboru.
Krok 1 — Načtení všech metadat z dokumentu
Následující pomocná funkce otevře soubor, projde celý strom vlastností a vrátí jednoduchou mapu JavaScriptu název vlastnosti → hodnota. Používá AnySpecification k zachycení jak vestavěných, tak vlastních polí.
/** Extract all metadata from a file */
function extractAllMetadata(documentPath) {
const result = {};
const metadata = new groupdocs.Metadata(documentPath);
try {
const properties = metadata.findProperties(new groupdocs.AnySpecification());
for (let i = 0; i < properties.getCount(); i++) {
const p = properties.get_Item(i);
const name = p.getName();
const value = valueToString(p);
result[name] = value;
}
} finally {
metadata.close();
}
return result;
}
Poznámka k výkonu: extrakce metadat z 5 MB DOCX trvá méně než 200 ms na typickém 2 GHz CPU [Docs][DOCS_URL].
Krok 2 — Vyhledání rozdílů mezi dvěma revizemi
Když jsou obě verze reprezentovány jako jednoduché objekty, rutina diff vytvoří tři skupiny — added, removed a changed — a poskytne pohodlný getter totalChanges.
/** Compare two metadata maps */
function compareMetadataSets(pathV1, pathV2) {
const v1 = extractAllMetadata(pathV1);
const v2 = extractAllMetadata(pathV2);
const added = {};
const removed = {};
const changed = {};
for (const k of Object.keys(v2)) {
if (!(k in v1)) added[k] = v2[k];
else if (v1[k] !== v2[k]) changed[k] = { from: v1[k], to: v2[k] };
}
for (const k of Object.keys(v1)) {
if (!(k in v2)) removed[k] = v1[k];
}
return {
added,
removed,
changed,
get totalChanges() {
return Object.keys(this.added).length + Object.keys(this.removed).length + Object.keys(this.changed).length;
},
};
}
Krok 3 — Vlastnictví a revizní informace (volitelné)
Detekce změn vlastnictví
Auditoři často sledují, kdo dokument vytvořil nebo upravil. Tato pomocná funkce izoluje příslušná pole a označí jakoukoli odchylku.
/** Ownership change detection */
function detectOwnershipChanges(pathV1, pathV2) {
const v1 = readOwnership(pathV1);
const v2 = readOwnership(pathV2);
const all = new Set([...Object.keys(v1), ...Object.keys(v2)]);
const changes = {};
for (const k of all) {
const oldV = v1[k] !== undefined ? v1[k] : '<missing>';
const newV = v2[k] !== undefined ? v2[k] : '<missing>';
if (oldV !== newV) changes[k] = { from: oldV, to: newV };
}
return changes;
}
Detekce změn revizní historie
Číslo revize, celkový čas úprav a časové razítko posledního tisku odhalují skrytou aktivitu.
/** Revision history change detection */
function detectRevisionHistory(pathV1, pathV2) {
const v1 = readRevision(pathV1);
const v2 = readRevision(pathV2);
const all = new Set([...Object.keys(v1), ...Object.keys(v2)]);
const changes = {};
for (const k of all) {
const oldV = v1[k] !== undefined ? v1[k] : '<missing>';
const newV = v2[k] !== undefined ? v2[k] : '<missing>';
if (oldV !== newV) changes[k] = { from: oldV, to: newV };
}
return changes;
}
Krok 4 — Export auditních zpráv
Export do CSV
Formát CSV je ideální pro Excel nebo ingest do SIEM. Pole jsou escapována, aby zvládla čárky a nové řádky.
/** Export diff to CSV */
function exportDiffToCsv(diff, outputPath) {
const rows = ['change_type,property,old_value,new_value'];
for (const [k, v] of Object.entries(diff.added)) rows.push(`added,${esc(k)},,${esc(v)}`);
for (const [k, v] of Object.entries(diff.removed)) rows.push(`removed,${esc(k)},${esc(v)},`);
for (const [k, o] of Object.entries(diff.changed)) rows.push(`changed,${esc(k)},${esc(o.from)},${esc(o.to)}`);
fs.writeFileSync(outputPath, rows.join('\n') + '\n', 'utf-8');
}
Export do JSON
JSON zachovává hierarchickou strukturu pro programové zpracování.
/** Export diff to JSON */
function exportDiffToJson(diff, outputPath) {
const payload = { added: diff.added, removed: diff.removed, changed: diff.changed };
fs.writeFileSync(outputPath, JSON.stringify(payload, null, 2), 'utf-8');
}
Kompletní řídící skript
Skript spojuje všechny části, vypíše rychlé shrnutí a zapíše jak CSV, tak JSON zprávy.
const path = require('path');
const fs = require('fs');
const groupdocs = require('@groupdocs/groupdocs.metadata');
function esc(txt) { return /[