💡 Volledig werkend voorbeeld beschikbaar op GitHub: office-metadata-pii-cleanup-nodejs

Introductie

Een upload‑endpoint accepteert een DOCX van een medewerker en slaat deze op bij een klantticket. De tekst is in orde. De eigenschappen niet: het bestand bevat de naam van de persoon die het heeft opgesteld, de collega die het als laatste heeft opgeslagen, de afdelingsmanager uit de bedrijfs‑template, en, omdat het van SharePoint kwam, de goedkeurder die het heeft ondertekend.

Een metadata‑sanitizer is een klein script dat die eigenschappen verwijdert voordat het bestand wordt opgeslagen en vervolgens zijn eigen werk controleert. Deze tutorial bouwt er één in Node.js met GroupDocs.Metadata, in vier stappen: eigenschappen selecteren op tag, ze selecteren op naam, alles wissen wanneer selectiviteit niet meer helpt, en verifiëren wat er overblijft. Elke stap bestaat uit een paar regels, en het voltooide script telt minder dan honderd.

Waarom metadata‑sanitisatie belangrijk is

De gegevens stapelen zich op zonder dat iemand ze kiest. Word schrijft Author en LastSavedBy vanuit het besturingssysteemaccount bij elke opslag, houdt een revisieteller bij, volgt TotalEditingTime en registreert LastPrinted. Documentservers voegen workflow‑paden, goedkeurders‑identifiers en content‑type‑URI’s toe bij het inchecken. Niets hiervan verschijnt wanneer het document wordt gelezen of afgedrukt, dus een proeflezer merkt het nooit.

Het voordeel van doen in Node.js in plaats van handmatig is dat een script cijfers teruggeeft: elke verwijderingsaanroep meldt hoeveel eigenschappen het heeft verwijderd, en dat aantal kan naar een log worden geschreven, in een test worden geverifieerd, of aan het record waartoe het document behoort worden gekoppeld.

Er is een tweede reden, minder duidelijk totdat een batch‑taak draait. Handmatige opschoning is een beslissing die één keer per bestand wordt genomen door wie het ook maar behandelt, waardoor twee personen die hetzelfde type document sanitiseren verschillende resultaten opleveren. Een script legt de regel op één plek vast: dezelfde vier tags, dezelfde lijst met sub‑strings, identiek toegepast, of de wachtrij nu drie bestanden of drie duizend bevat.

Voorvereisten

Het pakket is Node.js via Java, dus de machine heeft een Java‑runtime naast Node nodig.

Installatie

npm install @groupdocs/groupdocs.metadata

Het voorbeeldproject zet versie 26.7 vast en voegt een overrides‑entry toe die nan instelt op ^2.22.0, waardoor de native binding op huidige Node‑releases kan worden gebouwd. Zonder licentiebestand draait de bibliotheek in evaluatiemodus, wat voldoende is om elke stap hier te volgen.

Stap 1 - Eigenschappen selecteren op basis van hun betekenis

Eigenschapsnamen verschillen tussen formaten en pakketten, dus de eerste regel matcht op tags. ContainsTagSpecification neemt een tag en matcht elke eigenschap die die tag draagt; .or() voegt specificaties samen tot één.

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);

Belangrijke punten:

  • Vier tags dekken de identiteitsgroep: maker, editor, manager en het bedrijfs‑bedrijf‑veld.
  • Titel, Onderwerp en Trefwoorden blijven onaangeroerd, zodat een index die hierop baseert blijft werken.
  • removeProperties retourneert het aantal getroffen items in plaats van een boolean.

Omhul het geheel met try/finally en roep metadata.close() in de finally. De binding houdt het bestand open tot dat moment, en een lus zonder dit raakt de handvatten op.

Stap 2 - Eigenschappen selecteren op naam

Commentaarthreads, revisietellers en servervelden hebben geen tag. Voor die gevallen matcht WithNameSpecification(needle, false) elke eigenschap waarvan de naam de needle bevat, en een vier‑regelige builder koppelt er één per sub‑string aan:

let spec = null;
for (const needle of needles) {
  const s = new groupdocs.WithNameSpecification(needle, false /* fullMatch */);
  spec = spec ? spec.or(s) : s;
}
return spec;

Drie passes hergebruiken die builder met verschillende lijsten. Commentaren komen eerst:

const affected = metadata.removeProperties(
  nameContainsSpec(['Comment', 'Reviewer', 'Reviewed']));
metadata.save(outputPath);

De bewerkings‑tijdlijn is de groep die vaak wordt vergeten, en het is degene die beschrijft hoe het document is geproduceerd:

const affected = metadata.removeProperties(nameContainsSpec([
  'Revision', 'TrackedChange', 'LastPrinted', 'TotalEditingTime', 'EditTime',
]));
metadata.save(outputPath);

De SharePoint‑pass is dezelfde aanroep met Server, Workflow, Approver, ContentType en Template. Sub‑string matching is opzettelijk: het vangt CommentsCount naast Comment zonder een exacte‑naam lijst per formaat bij te houden.

Stap 3 - Alles wissen wanneer selectiviteit niet meer helpt

Voor de kopie die de organisatie verlaat, vervangt één aanroep de vier passes:

const affected = metadata.sanitize();
metadata.save(outputPath);

sanitize() wist elk metadata‑pakket dat de bibliotheek detecteert, inclusief aangepaste OOXML‑onderdelen, en het aantal overschrijdt meestal de som van de gerichte passes. Het neemt ook Titel en Onderwerp, daarom hoort het aan de grens en niet in een review‑lus.

Stap 4 - Verifiëren, want een stil gemiste fout lijkt op succes

De scan hergebruikt dezelfde specificaties via findProperties, die leest zonder te schrijven. Het resultaat is een Java‑collectie, dus die wordt doorlopen met een index:

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}`);
}

Het leeg‑en‑nul‑filter verdient zijn plaats. Ik heb het toegevoegd nadat een uitvoering mislukte op een revisieteller die op 0 was gezet, wat de scan trouw rapporteerde als een overgebleven eigenschap.

Volledig werkend voorbeeld

De repository koppelt de zes functies aan index.js, die de licentie toepast, elke pass uitvoert tegen resources/pii-sample.docx, controleert dat elk uitvoerbestand bestaat, en eindigt met een controle dat de lek‑lijst leeg is. Een mislukte assertie stopt met een niet‑nul code, zodat het geheel werkt als een controle in CI in plaats van een demo die je leest.

Een detail dat het waard is om in je eigen versie te kopiëren: elke pass leest hetzelfde bronbestand en schrijft een aparte output, in plaats van één opgeschoond bestand door te geven aan de volgende. Zo blijven de aantallen getroffen items onafhankelijk, waardoor een logregel voor de comment‑pass rapporteert wat de comment‑regel vond en niet wat er overbleef na de identiteits‑regel.

Wanneer moet ik een gerichte pass uitvoeren in plaats van sanitize()?

Telkens wanneer het document nog in gebruik is. Bestanden die tussen beoordelaars circuleren, vertrouwen op Titel, Onderwerp en Trefwoorden voor zoeken en classificatie, en sanitize() verwijdert alle drie samen met de persoonlijke gegevens. Voer de identiteits‑ en comment‑passes uit tijdens samenwerking, houd de beschrijvende velden intact, en bewaar de volledige wis‑actie voor de kopie die daadwerkelijk vertrekt.

Praktische toepassingen

Upload‑handler

Een Express‑route saniteert een bijlage voordat deze naar opslag wordt geschreven, registreert de getroffen aantallen op het ticket, en weigert de upload wanneer de lek‑lijst niet leeg is.

Nachtelijke export‑taak

Een worker doorloopt een exportmap, past de identiteits‑ en server‑passes toe, en laat de taak falen in plaats van een waarschuwing te loggen wanneer een document nog rest‑PII rapporteert.

Pre‑publicatiepoort

Een build‑stap saniteert documentatie‑bijlagen vóór release, met sanitize() omdat niets in die bestanden hun metadata bewaard moet blijven.

Best practices en tips

  • Schrijf altijd naar een nieuw pad zodat het origineel behouden blijft voor geschiloplossing.
  • Sluit het metadata‑object in een finally‑blok, vooral in lussen.
  • Voeg specificaties samen met .or() in batch‑taken; één open en één save is beter dan vier.
  • Log het aantal getroffen items per pass, inclusief nullen, zodat een niet‑herkend formaat zichtbaar wordt.

Veelvoorkomende problemen oplossen

Het aantal getroffen items is nul op een document waarvan je weet dat het vuil is

Controleer of het invoerformaat wordt herkend voordat je concludeert dat het bestand schoon is; een onleesbaar bestand en een schoon bestand geven beide nul.

De lek‑controle rapporteert eigenschappen die je net hebt verwijderd

Richt het op het opgeslagen uitvoerpad, niet op het invoerpad. De scan leest welk bestand ook wordt opgegeven.

Commentaarballonnen zijn nog steeds zichtbaar in Word

Commentaartekst bevindt zich in de document‑body, niet in een metadata‑pakket. GroupDocs.Metadata wist de commentaar‑gerelateerde eigenschappen; het verwijderen van de ballonnen zelf vereist een content‑bewerkingsbibliotheek zoals Aspose.Words.

Conclusie

Vier stappen, zes functies, één script dat rapporteert wat het heeft gedaan. Tag‑specificaties behandelen de identiteitsgroep over formaten heen, naam‑specificaties de families die tags niet classificeren, sanitize() behandelt de grens, en de lek‑scan maakt van het geheel een controle. Clone de repository, voer het uit tegen een document dat een echte review‑ronde heeft doorlopen, en bekijk de aantallen voordat je beslist welke passes jouw pipeline nodig heeft.

Aanvullende bronnen