💡 Volledig werkend voorbeeld beschikbaar op GitHub:
remove-pii-from-office-metadata-java
De compliance‑uitdaging: waarom handmatige metadata‑review faalt op schaal
Een records‑team levert 200 documenten aan een externe auditor. Iemand heeft elke pagina gelezen. Niemand heeft de eigenschappen gelezen, en juist daar zit de persoonlijke data: de analist in Author, de tweede analist in LastSavedBy, een afdelingshoofd in Manager, de dochteronderneming in Company, een LastPrinted‑tijdstempel van de avond vóór de deadline, en, bij alles dat via SharePoint is gegaan, een goedkeurder‑ID en een workflow‑pad.
Metadata‑sanitizing is een GroupDocs.Metadata‑workflow voor Java die die identiteits‑dragende eigenschappen uit Word‑, Excel‑ en PowerPoint‑bestanden verwijdert en vervolgens het resultaat terugleest om te rapporteren wat er overblijft. Dit artikel loopt door de workflow zoals een compliance‑team die zou bouwen: welke eigenschapsgroepen bestaan, welke verwijderingsregel bij elke groep past, wanneer een volledige wisactie de gerichte passes vervangt, en waarom verificatie in dezelfde taak hoort in plaats van in een checklist.
Het schaalprobleem is niet dat verwijderen moeilijk is. Het is dat handmatige review geen record oplevert. Een auditor die vraagt “welke velden zijn uit dit bestand verwijderd, en wanneer” heeft een cijfer nodig, en een eigenschappen‑dialoog levert dat niet.
Waarom generieke opruim‑tools hier niet werken
Het Windows‑eigenschappen‑dialoog bewerkt één bestand per keer en bereikt slechts een subset van velden. De Document Inspector van Office draait interactief, waardoor hij niet in een nacht‑taak past. Beide laten aangepaste OOXML‑onderdelen onaangeroerd, en geen van beiden schrijft iets dat een pipeline kan teruglezen.
Teams die een niveau dieper gaan, door docProps/core.xml en docProps/custom.xml direct te bewerken, nemen een onderhouds‑last op zich: een XPath‑expressie per veld, per formaat, die opnieuw moet worden bekeken telkens wanneer een producerende applicatie een naam wijzigt. Dat werk maakt ook de classificatie‑vraag fout. Eigenschapsnamen verschillen per pakket, waardoor een namenlijst stilletjes veroudert, en een regel die niet meer overeenkomt ziet er hetzelfde uit als een bestand dat al schoon was.
De oplossing: GroupDocs.Metadata in een records‑workflow
GroupDocs.Metadata voor Java voert alles uit via één eigenschap‑zoekmachine. Een Specification‑object bepaalt welke eigenschappen overeenkomen, removeProperties verwijdert elke match en geeft het aantal getroffen items terug, en findProperties voert dezelfde predicaat‑logica alleen‑lezen uit. Eigenschappen dragen tags, zodat Tags.getPerson().getCreator() auteur‑achtige velden identificeert, ongeacht het formaat of pakket waar ze vandaan komen.
Java heeft geen lambda‑overload voor removeProperties, wat hier een voordeel blijkt te zijn: elke regel is een object, en objecten zijn herbruikbaar. Dezelfde specificatie‑instantie die een groep wist, kan worden doorgegeven aan de verificatiescan, zodat de controle niet kan afdrijven van de opruiming die hij moet testen.
Implementatie van de sanitizing‑pipeline stap voor stap
Stap 1 – Wis de identiteits‑groep op tag
Vier tag‑specificaties samengevoegd met .or(...) dekken creator, editor, manager en company. Niets anders wordt verplaatst, dus Title, Subject en Keywords blijven beschikbaar voor de records‑index.
try (Metadata metadata = new Metadata(inputPath)) {
if (metadata.getFileFormat() == FileFormat.Unknown) return 0;
int affected = metadata.removeProperties(
new ContainsTagSpecification(Tags.getPerson().getCreator())
.or(new ContainsTagSpecification(Tags.getPerson().getEditor()))
.or(new ContainsTagSpecification(Tags.getPerson().getManager()))
.or(new ContainsTagSpecification(Tags.getCorporate().getCompany())));
metadata.save(outputPath);
return affected;
}
De FileFormat.Unknown‑guard is belangrijker dan het lijkt. Zonder deze guard geeft een onleesbaar bestand nul verwijderingen terug, wat de aanroeper niet kan onderscheiden van een document dat bij aankomst al schoon was.
Stap 2 – Wis veldfamilies op naam
Reactie‑threads, revisie‑tellers en server‑velden hebben geen tag, dus worden ze op naam gematcht. Een kleine Specification‑subklasse neemt een varargs‑lijst van substrings, waardoor één klasse drie passes kan bedienen:
public class NameContainsSpec extends Specification {
private final String[] needles;
public NameContainsSpec(String... needles) {
this.needles = needles;
}
@Override
public boolean isSatisfiedBy(MetadataProperty candidate) {
String name = candidate.getName();
if (name == null) return false;
for (String n : needles) {
if (name.contains(n)) return true;
}
return false;
}
}
De bewerkings‑tijdlijn is de groep‑compliance‑teams het meest interesseert, omdat het aantal revisies en de laatste afdrukdatum beschrijven hoe een document is geproduceerd:
try (Metadata metadata = new Metadata(inputPath)) {
if (metadata.getFileFormat() == FileFormat.Unknown) return 0;
int affected = metadata.removeProperties(new NameContainsSpec(
"Revision", "TrackedChange", "LastPrinted", "TotalEditingTime", "EditTime"));
metadata.save(outputPath);
return affected;
}
De comment‑pass en de SharePoint‑pass zijn dezelfde oproep met verschillende substring‑lijsten: Comment, Reviewer, Reviewed voor review‑sporen, en Server, Workflow, Approver, ContentType, Template voor document‑server‑velden.
Stap 3 – Wis aan de grens
Wanneer een bestand de organisatie verlaat, stopt selectiviteit met betalen. Eén oproep wist elk metadata‑pakket dat de bibliotheek detecteert, inclusief aangepaste OOXML‑onderdelen waar geen gerichte predicaat naar zoekt:
try (Metadata metadata = new Metadata(inputPath)) {
if (metadata.getFileFormat() == FileFormat.Unknown) return 0;
int affected = metadata.sanitize();
metadata.save(outputPath);
return affected;
}
Stap 4 – Verifieer en classificeer wat overblijft
De scan voert de unie van elke regel uit via findProperties en sorteert de hits in twee lijsten. Lege waarden en nul‑tellers worden overgeslagen, en items waarvan de naam begint met Comment, Revision of Inspection zijn wrappers rond de inhoud in plaats van metadata:
String value = "";
if (p.getValue() != null && p.getValue().getRawValue() != null) {
value = String.valueOf(p.getValue().getRawValue());
}
if (value.isEmpty() || value.equals("0") || value.equals("0.0")) continue;
String entry = p.getName() + "=" + value;
String name = p.getName() == null ? "" : p.getName();
if (name.startsWith("Comment") || name.startsWith("Revision")
|| name.startsWith("Inspection")) {
report.contentLevelLeaks.add(entry);
} else {
report.metadataLeaks.add(entry);
}
De splitsing is wat het pass/fail‑signaal eerlijk houdt. Metadata‑lekkages moeten leeg zijn. Content‑level‑lekkages blijven informatief, omdat Word‑commentaren en auteurs van tracked changes zich binnen word/document.xml bevinden, en een metadata‑bibliotheek rapporteert ze zonder ze te bewerken; het verwijderen daarvan vereist een content‑bewerkings‑bibliotheek zoals Aspose.Words.
Wanneer is een gerichte pass beter dan een volledige sanitizing?
Telkens wanneer het document nog in gebruik is. Een bestand dat tussen reviewers circuleert heeft zijn Title, Subject en Keywords nodig voor zoeken en records‑classificatie, en sanitize() verwijdert al deze drie. Voer de identity‑ en comment‑passes uit tijdens samenwerking, behoud de beschrijvende velden, en bewaar de volledige wisactie voor het moment dat het bestand de grens naar een externe partij overschrijdt.
Werkelijke workflow: een export‑taak voor een externe auditor
Stel je de nacht‑taak voor. Hij leest een lijst met document‑ID’s, kopieert elk bestand naar een staging‑pad, past de identity‑ en server‑passes toe, roept sanitize() aan op alles dat gemarkeerd is als vertrekkend uit de organisatie, en voert vervolgens de lek‑check uit tegen de opgeslagen kopie. Elke stap draagt zijn aantal getroffen items bij aan één log‑regel per bestand, en een niet‑lege metadata‑leak‑lijst laat de taak falen in plaats van een waarschuwing te loggen.
Ik heb ooit een middag besteed aan een versie van deze taak die nul verwijderingen rapporteerde over 40 bestanden en eruitzag als een schone batch. De invoermap bevatte legacy .doc‑binaire bestanden, de format‑guard gaf vroegtijdig bij elk van hen een terug, en er stond niets in de log dat “niets te verwijderen” onderscheidde van “niets werd gelezen”. Het loggen van het formaat naast het aantal loste het probleem.
Zakelijke impact: wat dit verandert
| Aspect | Handmatige review | GroupDocs.Metadata pipeline |
|---|---|---|
| Dekking | velden zichtbaar in het eigenschappen‑dialoog | elk pakket dat de bibliotheek detecteert, inclusief aangepaste OOXML‑onderdelen |
| Record van het werk | notities, als iemand ze schreef | aantal getroffen per bestand en per eigenschapsgroep |
| Herhaalbaarheid | afhankelijk van wie het doet | één specificatie per regel, identiek toegepast op elk bestand |
| Verificatie | bestand opnieuw openen en kijken | findProperties‑scan met een tweerichtings‑classificatie |
| Schaal | één bestand per keer | dezelfde zes bewerkingen in een lus over een export‑map |
Andere scenario’s waarin GroupDocs.Metadata past
Dezelfde eigenschap‑engine leest net zo goed als hij verwijdert. Het vergelijken van metadata tussen twee versies van een document laat zien wat een bewerkingsronde heeft veranderd, wat nuttig is bij eigendoms‑geschillen en bij het opsporen van een bestand dat buiten het proces opnieuw is geautoriseerd. Werken met metadata tags in plaats van namen maakt beide gevallen draagbaar over DOCX, XLSX, PPTX, PDF en afbeeldingsformaten.
Aan de slag met GroupDocs.Metadata voor Java
Voeg de GroupDocs‑Java‑repository toe aan pom.xml en maak een afhankelijkheid op com.groupdocs:groupdocs-metadata. De bibliotheek draait in evaluatiemodus zonder licentie, wat voldoende is om alle zes bewerkingen uit te voeren op een voorbeeld‑DOCX en de aantallen te zien. Begin met de identity‑pass, voeg de veld‑familie‑passes toe naarmate je documentbronnen ze vereisen, en koppel de lek‑check in voordat een van beide naar productie gaat.