💡 Exemplo completo disponível no GitHub:
office-metadata-pii-cleanup-nodejs
Introdução
Um endpoint de upload aceita um DOCX de um membro da equipe e o armazena em um ticket de cliente. O texto está ok. As propriedades não estão: o arquivo nomeia a pessoa que o redigiu, o colega que o salvou pela última vez, o gerente do departamento do modelo corporativo e, como ele veio do SharePoint, o aprovador que o assinou.
Um sanitizador de metadados é um pequeno script que exclui essas propriedades antes que o arquivo seja armazenado e, em seguida, verifica seu próprio trabalho. Este tutorial cria um em Node.js com GroupDocs.Metadata, em quatro etapas: selecionar propriedades por tag, selecioná‑las por nome, apagar tudo quando a seletividade deixa de ajudar e verificar o que resta. Cada etapa tem poucas linhas, e o script final tem menos de cem linhas.
Por que a Sanitização de Metadados é Importante
Os dados se acumulam sem que ninguém os escolha. O Word grava Author e LastSavedBy a partir da conta do sistema operacional a cada salvamento, mantém um contador de revisões, rastreia TotalEditingTime e registra LastPrinted. Servidores de documentos adicionam caminhos de fluxo de trabalho, identificadores de aprovadores e URIs de tipo de conteúdo no check‑in. Nada disso aparece quando o documento é lido ou impresso, portanto uma revisão nunca os detecta.
O objetivo de fazer isso em Node.js em vez de manualmente é que um script devolve números: cada chamada de remoção informa quantas propriedades foram excluídas, e essa contagem pode ser gravada em um log, verificada em um teste ou anexada ao registro ao qual o documento pertence.
Há um segundo motivo, menos óbvio até que um job em lote esteja em execução. A limpeza manual é uma decisão feita uma vez por arquivo por quem quer que o esteja manipulando, de modo que duas pessoas sanitizando o mesmo tipo de documento produzem resultados diferentes. Um script fixa a regra em um único lugar: as mesmas quatro tags, as mesmas listas de substrings, aplicadas identicamente, seja a fila contendo três arquivos ou três mil.
Pré‑requisitos
O pacote é Node.js via Java, portanto a máquina precisa de um runtime Java ao lado do Node.
Instalação
npm install @groupdocs/groupdocs.metadata
O projeto de exemplo fixa a versão 26.7 e adiciona uma entrada overrides definindo nan para ^2.22.0, o que mantém a vinculação nativa funcionando nas versões atuais do Node. Sem um arquivo de licença a biblioteca roda em modo de avaliação, o que é suficiente para seguir todas as etapas aqui.
Etapa 1 - Selecionar propriedades pelo que significam
Os nomes das propriedades diferem entre formatos e pacotes, então a primeira regra combina por tags. ContainsTagSpecification recebe uma tag e combina com qualquer propriedade que a contenha; .or() mescla especificações em uma única.
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);
Pontos principais:
- Quatro tags cobrem o grupo de identidade: criador, editor, gerente e o campo corporativo da empresa.
- Title, Subject e Keywords permanecem intactos, de modo que um índice de registros que usa esses campos continua funcionando.
removePropertiesdevolve a contagem de itens afetados ao invés de um booleano.
Envolva tudo em try/finally com metadata.close() no finally. A vinculação mantém o arquivo aberto até então, e um loop sem isso esgota os handles.
Etapa 2 - Selecionar propriedades por nome
Fios de comentários, contadores de revisão e campos de servidor não carregam tag. Para eles, WithNameSpecification(needle, false) combina qualquer propriedade cujo nome contenha a substring, e um construtor de quatro linhas encadeia uma por substring:
let spec = null;
for (const needle of needles) {
const s = new groupdocs.WithNameSpecification(needle, false /* fullMatch */);
spec = spec ? spec.or(s) : s;
}
return spec;
Três passagens reutilizam esse construtor com listas diferentes. Comentários vão primeiro:
const affected = metadata.removeProperties(
nameContainsSpec(['Comment', 'Reviewer', 'Reviewed']));
metadata.save(outputPath);
A linha do tempo de edição é o grupo que costuma ser esquecido, e é o que descreve como o documento foi produzido:
const affected = metadata.removeProperties(nameContainsSpec([
'Revision', 'TrackedChange', 'LastPrinted', 'TotalEditingTime', 'EditTime',
]));
metadata.save(outputPath);
A passagem do SharePoint usa a mesma chamada com Server, Workflow, Approver, ContentType e Template. A correspondência por substring é deliberada: captura CommentsCount junto com Comment sem precisar manter uma lista de nomes exatos por formato.
Etapa 3 - Apagar tudo quando a seletividade deixa de ajudar
Para a cópia que deixa a organização, uma chamada substitui as quatro passagens:
const affected = metadata.sanitize();
metadata.save(outputPath);
sanitize() limpa todos os pacotes de metadados que a biblioteca detecta, incluindo partes OOXML personalizadas, e sua contagem geralmente supera a soma das passagens direcionadas. Ela também remove Title e Subject, por isso deve ser usada na fronteira e não dentro de um loop de revisão.
Etapa 4 - Verificar, porque uma falha silenciosa parece sucesso
A varredura reutiliza as mesmas especificações através de findProperties, que lê sem escrever. O resultado é uma coleção Java, então ele é percorrido por índice:
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}`);
}
O filtro de vazio e zero ganha seu lugar. Eu o adicionei depois que uma execução falhou em um contador de revisão que havia sido zerado, o que a varredura reportou fielmente como uma propriedade sobrevivente.
Exemplo Completo de Funcionamento
O repositório conecta as seis funções em index.js, que aplica a licença, executa cada passagem contra resources/pii-sample.docx, verifica que cada arquivo de saída existe e finaliza afirmando que a lista de vazamentos está vazia. Uma asserção falha encerra o processo com código diferente de zero, de modo que tudo funciona como uma verificação em CI ao invés de uma demonstração para leitura.
Um detalhe vale a pena copiar para sua própria versão: cada passagem lê o mesmo arquivo fonte e grava uma saída separada, ao invés de encadear um arquivo limpo no próximo. Isso mantém as contagens de itens afetados independentes, de modo que a linha de log da passagem de comentários relata o que a regra de comentários encontrou, e não o que restou após a regra de identidade ser executada.
Quando devo executar uma passagem direcionada em vez de sanitize()?
Sempre que o documento ainda estiver em uso. Arquivos circulando entre revisores dependem de Title, Subject e Keywords para busca e classificação, e sanitize() remove os três junto com os dados pessoais. Execute as passagens de identidade e comentário durante a colaboração, mantenha os campos descritivos intactos e reserve a limpeza completa para a cópia que realmente sai.
Aplicações no Mundo Real
Manipulador de Upload
Uma rota Express sanitiza um anexo antes de gravá‑lo no armazenamento, registra as contagens afetadas no ticket e rejeita o upload quando a lista de vazamentos não está vazia.
Trabalho de exportação noturna
Um worker percorre uma pasta de exportação, aplica as passagens de identidade e servidor, e falha o job ao invés de apenas registrar um aviso quando um documento ainda relata PII residual.
Porta de pré‑publicação
Uma etapa de build sanitiza anexos de documentação antes do release, usando sanitize() porque nada nesses arquivos precisa que seus metadados sejam preservados.
Melhores Práticas e Dicas
- Sempre grave em um caminho novo para que o original sobreviva para resolução de disputas.
- Feche o objeto de metadados em um bloco
finally, especialmente em loops. - Mescle especificações com
.or()em jobs em lote; uma abertura e um salvamento valem mais que quatro. - Registre a contagem de itens afetados por passagem, inclusive zeros, para que um formato não reconhecido seja visível.
Solução de Problemas de Problemas Comuns
A contagem de itens afetados é zero em um documento que você sabe que está sujo
Verifique se o formato de entrada foi reconhecido antes de concluir que o arquivo estava limpo; um arquivo não lido e um arquivo limpo produzem o mesmo zero.
A verificação de vazamento relata propriedades que você acabou de remover
Aponte-a para o caminho de saída salvo, não para o caminho de entrada. A varredura lê o arquivo que lhe for fornecido.
Balões de comentário ainda aparecem no Word
O texto do comentário vive no corpo do documento, não em um pacote de metadados. GroupDocs.Metadata limpa as propriedades relacionadas a comentários; remover os balões em si requer uma biblioteca de edição de conteúdo como Aspose.Words.
Conclusão
Quatro etapas, seis funções, um script que relata o que fez. Especificações de tag tratam o grupo de identidade entre formatos, especificações de nome cobrem as famílias que as tags não classificam, sanitize() lida com a fronteira, e a varredura de vazamento transforma tudo em uma verificação. Clone o repositório, execute-o contra um documento que passou por uma rodada real de revisão e observe as contagens antes de decidir quais passagens sua pipeline precisa.