💡 Esempio completo funzionante disponibile su GitHub:
metadata-diff-doc-versions-using-groupdocs-metadata-nodejs

Introduzione

Il confronto dei metadati è una funzionalità di GroupDocs.Metadata che rivela modifiche nascoste tra versioni di documenti, consentendo agli auditor di verificare rapidamente l’autenticità. Quando i team legali devono dimostrare che un contratto non è stato manomesso, le proprietà invisibili — autore, editore, timestamp di revisione — spesso raccontano la vera storia.

Mi sono imbattuto in questo problema esaminando un lotto di accordi con fornitori; ogni file dichiarava la stessa data di creazione, ma i campi autore nascosti differivano, e l’audit manuale avrebbe richiesto ore.

Perché i metadati nascosti sono importanti per i team legali

I professionisti legali e di conformità si affidano a prove immutabili. Anche se il testo visibile rimane lo stesso, una modifica al campo Creator o LastPrinted può indicare modifiche non autorizzate. Rilevare questi cambiamenti in anticipo previene controversie costose e soddisfa i percorsi di audit normativi.

La nostra soluzione con GroupDocs.Metadata

GroupDocs.Metadata per Node.js fornisce un’unica API che estrae ogni proprietà supportata, confronta due revisioni e genera report di diff strutturati. L’SDK gestisce DOCX, PDF, XLSX e molti altri formati out‑of‑the‑box, così non è necessario creare parser personalizzati per ogni tipo di file.

Passo 1 – Estrarre tutti i metadati da un documento

Il helper qui sotto apre un file, percorre l’intero albero delle proprietà e restituisce una semplice mappa JavaScript di nome proprietà → valore. Usa AnySpecification per catturare sia i campi integrati sia quelli personalizzati.

/** 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;
}

Nota sulle prestazioni: l’estrazione dei metadati da un DOCX di 5 MB si completa in meno di 200 ms su una tipica CPU da 2 GHz [Docs][DOCS_URL].

Passo 2 – Individuare le differenze tra due revisioni

Con entrambe le versioni rappresentate come oggetti semplici, la routine di diff costruisce tre gruppi — added, removed e changed — e fornisce un comodo 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;
    },
  };
}

Passo 3 – Approfondimenti su proprietà e revisioni (opzionale)

Rilevamento delle modifiche di proprietà

Gli auditor legali spesso si concentrano su chi ha creato o modificato un documento. Questo helper isola quei campi e segnala qualsiasi variazione.

/** 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;
}

Rilevamento delle modifiche alla cronologia delle revisioni

Numero di revisione, tempo totale di editing e timestamp di ultima stampa rivelano attività nascoste.

/** 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;
}

Passo 4 – Esportare i report di audit

Esportazione CSV

Il formato CSV è ideale per Excel o per l’ingestione in SIEM. I campi sono escapati per gestire virgole e newline.

/** 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');
}

Esportazione JSON

JSON preserva la struttura gerarchica per il consumo programmatico.

/** 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');
}

Script completo

Lo script collega tutto, stampa un riepilogo rapido e scrive sia i report CSV sia JSON.

const path = require('path');
const fs = require('fs');
const groupdocs = require('@groupdocs/groupdocs.metadata');

function esc(txt) { return /[",\n]/.test(txt) ? `"${txt.replace(/"/g, '""')}"` : txt; }

const diff = compareMetadataSets('resources/document-v1.docx', 'resources/document-v2.docx');
console.log(`Total metadata changes: ${diff.totalChanges}`);

exportDiffToCsv(diff, path.resolve('metadata-diff.csv'));
exportDiffToJson(diff, path.resolve('metadata-diff.json'));

Eseguendo node index.js vengono creati metadata-diff.csv e metadata-diff.json nella radice del progetto.

Come posso confrontare i metadati tra due versioni di un documento?

Confronti i metadati caricando ogni revisione con la classe Metadata,