💡 Exemple complet fonctionnel disponible sur GitHub :
office-metadata-pii-cleanup-nodejs
Introduction
Un point de terminaison d’envoi accepte un DOCX d’un membre du personnel et le stocke dans le ticket d’un client. Le texte est correct. Les propriétés ne le sont pas : le fichier indique le nom de la personne qui l’a rédigé, le collègue qui l’a enregistré en dernier, le responsable du département provenant du modèle d’entreprise, et, comme il provient de SharePoint, l’approbateur qui l’a validé.
Un nettoyeur de métadonnées est un petit script qui supprime ces propriétés avant que le fichier ne soit stocké, puis vérifie son propre travail. Ce tutoriel en crée un en Node.js avec GroupDocs.Metadata, en quatre étapes : sélectionner les propriétés par balise, les sélectionner par nom, tout effacer lorsque la sélectivité n’aide plus, et vérifier ce qui reste. Chaque étape ne comporte que quelques lignes, et le script final fait moins d’une centaine de lignes.
Pourquoi le nettoyage des métadonnées est important
Les données s’accumulent sans que personne ne les choisisse. Word écrit Author et LastSavedBy à partir du compte du système d’exploitation à chaque enregistrement, conserve un compteur de révisions, suit TotalEditingTime, et enregistre LastPrinted. Les serveurs de documents ajoutent des chemins de workflow, des identifiants d’approbateur et des URI de type de contenu lors du check‑in. Aucun de ces éléments n’apparaît lorsque le document est lu ou imprimé, donc une relecture ne les détecte jamais.
L’intérêt de le faire en Node.js plutôt qu’à la main, c’est qu’un script renvoie des nombres : chaque appel de suppression indique combien de propriétés il a supprimées, et ce compte peut être écrit dans un journal, vérifié dans un test, ou attaché à l’enregistrement auquel le document appartient.
Il y a une seconde raison, moins évidente tant qu’un travail par lots n’est pas en cours. Le nettoyage manuel est une décision prise une fois par fichier par la personne qui le traite, de sorte que deux personnes nettoyant le même type de document obtiennent des résultats différents. Un script fixe la règle en un seul endroit : les mêmes quatre balises, les mêmes listes de sous‑chaînes, appliquées de façon identique que la file d’attente contienne trois fichiers ou trois mille.
Prérequis
Le paquet fonctionne sous Node.js via Java, donc la machine a besoin d’une machine virtuelle Java en plus de Node.
Installation
npm install @groupdocs/groupdocs.metadata
Le projet d’exemple fixe la version 26.7 et ajoute une entrée overrides définissant nan à ^2.22.0, ce qui maintient la construction du binding natif sur les versions actuelles de Node. Sans fichier de licence, la bibliothèque s’exécute en mode d’évaluation, ce qui suffit pour suivre chaque étape présentée ici.
Étape 1 – Sélectionner les propriétés par ce qu’elles signifient
Les noms de propriétés diffèrent selon les formats et les paquets, donc la première règle s’appuie sur les balises. ContainsTagSpecification prend une balise et correspond à toute propriété qui la porte ; .or() fusionne les spécifications en une seule.
const T = groupdocs.Tags;
const spec = new groupdocs.ContainsTagSpecification(T.getPerson().getCreator())
.or(new groupdocs.ContainsTagSpecification(T.getPerson().getEditor()))
.or(new groupdocs.ContainsTagSpecification(T.getPerson().getManager()))
.or(new groupdocs.ContainsTagSpecification(T.getCorporate().getCompany()));
const affected = metadata.removeProperties(spec);
metadata.save(outputPath);
Points clés :
- Quatre balises couvrent le groupe d’identité : créateur, éditeur, responsable et le champ d’entreprise.
- Title, Subject et Keywords restent intacts, de sorte qu’un index de dossiers qui s’appuie sur eux continue de fonctionner.
removePropertiesrenvoie le nombre d’éléments affectés plutôt qu’un booléen.
Enveloppez le tout dans un try/finally avec metadata.close() dans le finally. Le binding maintient le fichier ouvert jusqu’alors, et une boucle sans cette fermeture épuisera les descripteurs.
Étape 2 – Sélectionner les propriétés par nom
Les fils de commentaires, les compteurs de révision et les champs serveur ne portent aucune balise. Pour ceux‑ci, WithNameSpecification(needle, false) correspond à toute propriété dont le nom contient l’aiguille, et un constructeur en quatre lignes enchaîne une spécification par sous‑chaîne :
let spec = null;
for (const needle of needles) {
const s = new groupdocs.WithNameSpecification(needle, false /* fullMatch */);
spec = spec ? spec.or(s) : s;
}
return spec;
Trois passes réutilisent ce constructeur avec des listes différentes. Les commentaires passent en premier :
const affected = metadata.removeProperties(
nameContainsSpec(['Comment', 'Reviewer', 'Reviewed']));
metadata.save(outputPath);
La chronologie d’édition est le groupe qui a tendance à être oublié, et c’est celui qui décrit comment le document a été produit :
const affected = metadata.removeProperties(nameContainsSpec([
'Revision', 'TrackedChange', 'LastPrinted', 'TotalEditingTime', 'EditTime',
]));
metadata.save(outputPath);
La passe SharePoint utilise le même appel avec Server, Workflow, Approver, ContentType et Template. La correspondance par sous‑chaîne est délibérée : elle capture CommentsCount en même temps que Comment sans devoir maintenir une liste de noms exacts pour chaque format.
Étape 3 – Tout effacer lorsque la sélectivité n’aide plus
Pour la copie qui quitte l’organisation, un seul appel remplace les quatre passes :
const affected = metadata.sanitize();
metadata.save(outputPath);
sanitize() supprime chaque paquet de métadonnées que la bibliothèque détecte, y compris les parties OOXML personnalisées, et son compteur dépasse généralement la somme des passes ciblées. Il supprime également Title et Subject, ce qui explique pourquoi il doit être exécuté à la frontière plutôt que dans une boucle de révision.
Étape 4 – Vérifier, car une omission silencieuse ressemble à un succès
L’analyse réutilise les mêmes spécifications via findProperties, qui lit sans écrire. Le résultat est une collection Java, donc on l’itère par indice :
const props = metadata.findProperties(tagSpec.or(nameSpec));
for (let i = 0; i < props.getCount(); i++) {
const p = props.get_Item(i);
const val = p.getValue && p.getValue();
const value = val ? String(val.getRawValue ? val.getRawValue() : val) : '';
if (!value || value === '0' || value === '0.0') continue;
leaks.push(`${p.getName()}=${value}`);
}
Le filtre « vide ou zéro » mérite sa place. Je l’ai ajouté après qu’une exécution a échoué sur un compteur de révision qui avait été remis à 0, que le scan rapportait fidèlement comme propriété survivante.
Exemple complet fonctionnel
Le dépôt assemble les six fonctions dans index.js, qui applique la licence, exécute chaque passe sur resources/pii-sample.docx, vérifie que chaque fichier de sortie existe, et termine en s’assurant que la liste des fuites est vide. Une assertion échouée provoque une sortie avec un code non nul, de sorte que l’ensemble fonctionne comme une vérification dans le CI plutôt que comme une démonstration à lire.
Un détail vaut la peine d’être copié dans votre propre version : chaque passe lit le même fichier source et écrit une sortie distincte, plutôt que d’enchaîner un fichier nettoyé dans le suivant. Cela garde les comptes d’éléments affectés indépendants, de sorte qu’une ligne de journal pour la passe de commentaires indique ce que la règle de commentaires a trouvé, et non ce qui restait après l’exécution de la règle d’identité.
Quand devrais‑je exécuter une passe ciblée au lieu de sanitize() ?
Chaque fois que le document est encore en cours d’utilisation. Les fichiers circulant entre les réviseurs s’appuient sur Title, Subject et Keywords pour la recherche et la classification, et sanitize() supprime les trois ainsi que les données personnelles. Exécutez les passes d’identité et de commentaires pendant la collaboration, conservez les champs descriptifs intacts, et réservez l’effacement complet pour la copie qui quitte réellement l’organisation.
Applications concrètes
Gestionnaire d’envoi
Une route Express nettoie une pièce jointe avant de l’écrire en stockage, enregistre les comptes affectés sur le ticket, et rejette l’envoi lorsque la liste des fuites n’est pas vide.
Job d’exportation nocturne
Un worker parcourt un dossier d’export, applique les passes d’identité et serveur, et échoue le job plutôt que d’enregistrer un avertissement lorsqu’un document signale encore des PII résiduelles.
Porte d’avant‑publication
Une étape de build nettoie les pièces jointes de documentation avant la diffusion, en utilisant sanitize() parce que rien dans ces fichiers ne nécessite la conservation de leurs métadonnées.
Bonnes pratiques et astuces
- Écrivez toujours vers un nouveau chemin afin que l’original survive pour la résolution de litiges.
- Fermez l’objet metadata dans un bloc
finally, surtout dans les boucles. - Fusionnez les spécifications avec
.or()dans les jobs par lots ; une ouverture et un enregistrement valent mieux que quatre. - Consignez le nombre d’éléments affectés par passe, y compris les zéros, afin qu’un format non reconnu soit visible.
Dépannage des problèmes courants
Le nombre d’éléments affectés est zéro sur un document que vous savez sale
Vérifiez que le format d’entrée est reconnu avant de conclure que le fichier était propre ; un fichier illisible et un fichier propre produisent le même zéro.
Le contrôle de fuite signale des propriétés que vous venez de supprimer
Pointez‑le vers le chemin de sortie enregistré, pas vers le chemin d’entrée. Le scan lit le fichier qui lui est fourni.
Les bulles de commentaires restent visibles dans Word
Le texte du commentaire vit dans le corps du document, pas dans un paquet de métadonnées. GroupDocs.Metadata supprime les propriétés liées aux commentaires ; la suppression des bulles elles‑mêmes nécessite une bibliothèque d’édition de contenu telle qu’Aspose.Words.
Conclusion
Quatre étapes, six fonctions, un script qui rapporte ce qu’il a fait. Les spécifications de balises gèrent le groupe d’identité à travers les formats, les spécifications de nom couvrent les familles que les balises ne classifient pas, sanitize() traite la frontière, et le scan de fuite transforme le tout en une vérification. Clonez le dépôt, exécutez‑le sur un document ayant traversé une vraie ronde de révision, et examinez les comptes avant de décider quelles passes votre pipeline nécessite.