💡 Esempio completo funzionante disponibile su GitHub:
scrub-office-document-pii-dotnet
Il vecchio metodo era doloroso
La routine è così: apri il documento, File, Info, Check for Issues, Inspect Document, spunti le caselle, Remove All, salvi con un nuovo nome, chiudi, apri il successivo. Dopo quaranta file, qualcuno nota che Inspect Document ha anche rimosso il Title su cui l’indice dei record si basa, e che la copia inviata un’ora prima conteneva ancora un ID approvatore di SharePoint, perché il file era stato salvato da un’applicazione diversa che aveva riscritto il campo.
La rimozione di PII nei metadati è una funzionalità di GroupDocs.Metadata per .NET che elimina programmaticamente le proprietà contenenti dati di identità dai documenti Office e riporta ciò che rimane. La routine manuale fallisce su tre fronti: non scala oltre una manciata di file, è tutto‑o‑niente per quanto riguarda i campi da rimuovere e non produce alcuna registrazione di ciò che è stato eliminato. Questo articolo mostra la versione .NET dello stesso lavoro, un gruppo di proprietà alla volta.
Aiuta sapere cosa c’è realmente dentro. Un file Word che ha attraversato un ciclo di revisione tipicamente contiene Author e LastSavedBy dall’account Windows di chi lo ha salvato, Manager e Company dal modello aziendale, un contatore di revisioni, TotalEditingTime, un timestamp LastPrinted e contatori per i thread dei commenti. Aggiungendo SharePoint alla catena ottieni anche identificatori di approvatori, percorsi di workflow, URI di content‑type e il modello da cui il documento è stato creato. Nulla di tutto ciò è visibile nella pagina, e tutto viaggia nello stesso file.
C’è un modo migliore
Tutto in GroupDocs.Metadata per .NET passa attraverso un unico motore di ricerca delle proprietà. RemoveProperties accetta una lambda su MetadataProperty, elimina ogni proprietà accettata dalla lambda e restituisce il conteggio. FindProperties esegue la stessa lambda senza scrivere. Le proprietà hanno anche dei tag, così Tags.Person.Creator identifica i campi in stile autore attraverso formati e pacchetti, invece di confrontare nomi letterali che variano a seconda dell’applicazione produttrice.
Questo fornisce tre modalità di pulizia invece di un unico pulsante: un passaggio per i tag dei campi di identità, passaggi per i nomi di famiglie come commenti e revisioni, e Sanitize() quando nulla deve sopravvivere. Tutti e tre restituiscono numeri, e i numeri sono ciò che rende il passaggio verificabile.
Il nuovo modo: un predicato per gruppo di proprietà
Passo 1 – Cancella i nomi
Quattro controlli di tag coprono il gruppo di identità. I campi descrittivi rimangono intatti, che è la differenza rispetto a Remove All dell’Inspector del documento:
using (var metadata = new Metadata(inputPath))
{
if (metadata.FileFormat == FileFormat.Unknown) return 0;
var affected = metadata.RemoveProperties(p =>
p.Tags.Contains(Tags.Person.Creator) ||
p.Tags.Contains(Tags.Person.Editor) ||
p.Tags.Contains(Tags.Person.Manager) ||
p.Tags.Contains(Tags.Corporate.Company));
metadata.Save(outputPath);
return affected;
}
Il controllo FileFormat.Unknown è la guardia che mantiene onesto lo zero: senza di esso, un file illeggibile e un file pulito appaiono uguali al chiamante.
Passo 2 – Cancella le famiglie attorno a loro
Commenti, revisioni e campi server non hanno tag, quindi il predicato corrisponde ai nomi. La cronologia di modifica è il gruppo più spesso dimenticato, ed è quello che indica come è stato prodotto un documento:
using (var metadata = new Metadata(inputPath))
{
if (metadata.FileFormat == FileFormat.Unknown) return 0;
var affected = metadata.RemoveProperties(p =>
p.Name != null && (
p.Name.Contains("Revision") ||
p.Name.Contains("TrackedChange") ||
p.Name.Contains("LastPrinted") ||
p.Name.Contains("TotalEditingTime") ||
p.Name.Contains("EditTime")));
metadata.Save(outputPath);
return affected;
}
Il confronto di sottostringhe è deliberato: cattura CommentsCount insieme a Comment, e TotalEditingTime insieme a EditTime, senza mantenere una lista di nomi esatti per formato. Il passaggio SharePoint è la stessa chiamata con Server, Workflow, Approver, ContentType e Template.
Passo 3 – Cancella tutto, poi verifica il risultato
Al confine di fiducia, una chiamata sostituisce i quattro passaggi:
using (var metadata = new Metadata(inputPath))
{
if (metadata.FileFormat == FileFormat.Unknown) return 0;
var affected = metadata.Sanitize();
metadata.Save(outputPath);
return affected;
}
Poi arriva la parte per la quale la routine manuale non ha alcun equivalente. La scansione di verifica riutilizza i predicati di rimozione tramite FindProperties e ordina i sopravvissuti in due elenchi:
foreach (var p in props)
{
var value = p.InterpretedValue?.ToString() ?? p.Value?.ToString() ?? string.Empty;
if (string.IsNullOrWhiteSpace(value)) continue;
if (value == "0" || value == "0.0") continue;
var entry = $"{p.Name}={value}";
var name = p.Name ?? string.Empty;
if (name.StartsWith("Comment") || name.StartsWith("Revision"))
report.ContentLevelLeaks.Add(entry);
else
report.MetadataLeaks.Add(entry);
}
MetadataLeaks deve essere vuoto prima che un file sia considerato sanificato. ContentLevelLeaks è informativo: i commenti Word e gli autori delle modifiche tracciate risiedono in word/document.xml, che è contenuto del corpo, e la loro rimozione richiede una libreria di editing del contenuto come Aspose.Words anziché un’API di metadati.
Perché non chiamare semplicemente Sanitize() su tutto?
Perché la maggior parte dei documenti è ancora in uso. Sanitize() cancella ogni pacchetto rilevato, inclusi Title, Subject e Keywords, i campi su cui un sistema di archiviazione e un indice di ricerca fanno affidamento. Usa i passaggi mirati mentre un file circola internamente, mantieni attivi i metadati descrittivi e riserva la cancellazione totale per la copia che effettivamente lascia l’organizzazione.
Fianco a fianco: Prima vs. Dopo
| Ispezione manuale | GroupDocs.Metadata per .NET | |
|---|---|---|
| Selettività | Remove All, campi descrittivi inclusi | un predicato per gruppo di proprietà |
| Copertura | campi esposti dalla finestra di dialogo | ogni pacchetto rilevato dalla libreria, parti OOXML personalizzate incluse |
| Registrazione | nessuna | conteggio delle proprietà interessate restituito per operazione |
| Verifica | riapri e controlla | scansione FindProperties con elenchi di metadati e contenuti |
| Batch di 200 file | 200 click‑through | un ciclo, cinque operazioni, una riga di log per file |
La riga che cambia comportamento è la Registrazione. Quando ogni passaggio restituisce un conteggio, la sanificazione smette di essere un passaggio che qualcuno ricorda di fare e diventa un dato su cui la pipeline può fare asserzioni: una soglia in un test, un campo in una tabella di audit, una condizione che fa fallire un job notturno. È anche la riga che una routine manuale non può produrre a nessun livello di disciplina.
Esempio reale: Hook pre‑invio
Un portale di supporto consente allo staff di allegare documenti ai ticket dei clienti. Il gestore degli allegati ora esegue il passaggio di identità e quello server prima che il file venga salvato, registra entrambi i conteggi sul ticket e avvia il controllo delle perdite sulla copia salvata. Un elenco non vuoto di perdite di metadati rifiuta il caricamento con un messaggio che indica la proprietà offensiva, così la persona che allega il file lo scopre immediatamente anziché dopo che è arrivato al cliente.
Due dettagli rendono pratico questo hook. I passaggi scrivono su un nuovo percorso, così l’originale rimane nello storage del dipendente e nulla viene distrutto da uno step automatizzato. E i conteggi vanno nel record del ticket accanto all’allegato, il che significa che la risposta a “cosa è stato rimosso da questo documento” è un numero memorizzato anziché un’ipotesi su ciò che la pipeline di solito fa.
La prima volta che ho puntato quel controllo su un modello reale, è tornato un valore Manager che il passaggio di identità aveva rimosso pochi secondi prima e che il modello aziendale aveva riscritto subito al salvataggio. La chiamata di rimozione funzionava esattamente come documentato; il problema era la pipeline intorno, e solo la lettura di ritorno lo ha mostrato.
Cos’altro puoi fare con GroupDocs.Metadata?
Il medesimo motore di predicati legge. Confrontare le proprietà tra due versioni di un documento evidenzia cambi di proprietà e ri‑autori fuori dal processo di revisione, e la panoramica sulla pulizia dei metadati spiega dove uno strumento interattivo si inserisce ancora accanto a un passaggio guidato da API. Poiché il sistema di tag attraversa i formati, il predicato di identità scritto qui funziona anche su PDF, immagini e file audio senza modifiche.
Questa portabilità vale la pena pianificarla. Una regola di pulizia scritta come lambda su MetadataProperty è C# ordinario, quindi può vivere in una libreria condivisa, essere testata unitariamente su documenti di fixture e essere applicata dal servizio che ne ha bisogno: un endpoint di esportazione, un job pianificato di archiviazione, o un passaggio di build che sanifica gli allegati della documentazione prima del rilascio. Le regole rimangono in un unico posto; solo i punti di chiamata cambiano.
Conclusione
Quattro passaggi mirati, una sanificazione completa, una scansione di verifica. Questo set copre l’intervallo pratico per i documenti Office: mantieni i metadati descrittivi mentre il file è in circolazione, cancella tutto quando lascia l’organizzazione e dimostra il risultato in entrambi i casi. Clona il campione, eseguilo su un documento che ha attraversato un vero ciclo di revisione e leggi i conteggi delle proprietà interessate. Sono solitamente più alti del previsto, ed è proprio questo il punto.