Introdução

Um colega envia duas revisões de um contrato e pede que você compare as diferenças. Você coloca ambas no seu serviço de comparação, o resultado volta e tudo parece normal. O que você não viu é que um desses documentos continha uma imagem vinculada apontando para uma URL, e seu servidor contatou esse host no momento em que o arquivo foi aberto. Nada na saída indica que isso aconteceu.

Isso não é um defeito – é o que significa carregar um documento fielmente. Um arquivo OOXML pode referenciar uma imagem que está em um servidor web em vez de dentro do pacote, e tanto o Word quanto qualquer biblioteca que carregue o documento resolvem essa referência corretamente. O GroupDocs.Comparison for .NET expõe duas propriedades em LoadOptions que permitem decidir se isso acontece: SkipExternalResources e WhitelistedResources.

Juntas, elas oferecem três configurações, e este artigo compara todas as três – o padrão permissivo, bloqueando tudo, e bloqueando tudo exceto referências nomeadas. Ao final, você saberá qual escolher para uma determinada origem de documento e os dois erros que fazem essas configurações parecerem não funcionar.

💡 Exemplo completo em funcionamento: block-external-resources-on-document-load-dotnet – um projeto de console executável que serve as imagens referenciadas ele mesmo e registra cada requisição, para que você possa observar cada configuração em ação.

Onde as Referências Externas se Escondem

Antes de escolher uma configuração, vale a pena entender o que está sendo escolhido. Um .docx contém referências externas em dois locais distintos, e elas são fáceis de perder porque nenhuma delas é visível no texto do documento.

A primeira é um relacionamento em word/_rels/document.xml.rels que possui TargetMode="External" e uma URL absoluta. A imagem aparece no corpo como um desenho que aponta para o relacionamento por ID, de modo que a URL nunca aparece próximo ao conteúdo que afeta.

A segunda é um código de campo INCLUDEPICTURE no corpo do documento, contendo sua URL dentro de uma instrução de campo. O Word a resolve quando a página é renderizada; uma biblioteca de comparação a resolve quando o documento é carregado.

Ambos os mecanismos respeitam as duas opções de carregamento discutidas abaixo, o que importa porque um documento pode usar um ou ambos. Uma referência que você encontrou no arquivo de relacionamentos não prova que não exista uma segunda em um código de campo.

Abordagem 1: O Padrão – Referências Resolvidas

SkipExternalResources tem o valor padrão false, portanto um documento carregado sem configuração tem suas referências remotas resolvidas:

LoadOptions loadOptions = new LoadOptions
{
    SkipExternalResources = false
};

using (Comparer comparer = new Comparer(sourcePath, loadOptions))
{
    comparer.Add(targetPath, loadOptions);
    comparer.Compare(outputPath);
}

Isso oferece a maior fidelidade: os documentos comparados contêm tudo o que referenciam, exatamente como o Word os renderizaria. Para documentos que sua própria aplicação ou modelos produziram, onde cada URL de referência aponta para infraestrutura que você controla, essa é a escolha correta – e uma imagem vinculada ausente pode tornar a comparação ativamente enganosa.

O custo é que cada referência é contatada, quem quer que a tenha colocado lá. Há também um custo de tempo que não tem nada a ver com confiança: uma URL de referência que não resolve mais faz o carregamento aguardar toda a tentativa de conexão, em cada comparação.

Abordagem 2: Bloquear Todo Recurso Externo

Uma propriedade desliga a resolução de referências remotas para aquele documento:

LoadOptions loadOptions = new LoadOptions
{
    SkipExternalResources = true
};

using (Comparer comparer = new Comparer(sourcePath, loadOptions))
{
    comparer.Add(targetPath, loadOptions);
    comparer.Compare(outputPath);
}

Nenhuma requisição é emitida. As imagens referenciadas ficam ausentes do resultado e – isso é o que vale a pena deixar claro – nada mais muda. A configuração governa o que é carregado, não como as diferenças são encontradas, de modo que alterações textuais e estruturais entre os dois documentos são detectadas exatamente como antes. A única coisa que você perde é a capacidade de detectar uma mudança dentro de uma imagem referenciada, que nunca foi carregada.

Esta é a configuração a ser tratada como sua linha de base para documentos que você não criou: uploads de usuários em uma aplicação web, arquivos recebidos por e‑mail, qualquer coisa comparada em um agente de build onde uma requisição externa raramente é desejada. É tudo ou nada, porém – uma imagem vinculada que você realmente queria é bloqueada junto com as demais, e o resultado simplesmente a omite sem anunciar o fato.

Abordagem 3: Bloquear Tudo Exceto Referências Nomeadas

A terceira configuração é a que recompensa uma leitura atenta. WhitelistedResources recebe um List<string> e é consultada apenas quando SkipExternalResources é true:

LoadOptions loadOptions = new LoadOptions
{
    SkipExternalResources = true,
    WhitelistedResources = new List<string> { "includepicture-field.png" }
};

using (Comparer comparer = new Comparer(sourcePath, loadOptions))
{
    comparer.Add(targetPath, loadOptions);
    comparer.Compare(outputPath);
}

As entradas são fragmentos de URL, não nomes de arquivo. Cada uma é comparada com a URL de referência, e uma correspondência em qualquer parte dela permite esse recurso. Isso é o que torna a lista de permissões portátil: "includepicture-field.png" permite a imagem independentemente do esquema, host e caminho que a precedam, de modo que a mesma lista funciona em desenvolvimento e produção sem reescrita.

A mesma propriedade funciona no sentido inverso. Um fragmento curto ou genérico – logo.png, ou pior, .png – pode corresponder a referências que você nunca pretendia permitir. Escolha um fragmento específico o suficiente para identificar o único recurso que você deseja.

No exemplo de referência, essa configuração busca a imagem na lista de permissões e deixa a segunda imagem referenciada, que não tem entrada correspondente, bloqueada. O registro de requisições mostra três solicitações, onde o padrão permissivo produzia cinco, e nomeia apenas o arquivo na lista de permissões.

Qual Configuração Você Deve Usar?

Combine a configuração com a origem do documento. Documentos gerados pela sua própria aplicação ou pelos seus modelos podem manter o padrão, porque cada URL de referência aponta para infraestrutura que você já opera. Qualquer coisa que venha de fora – uploads de usuários, anexos de e‑mail, arquivos de terceiros – justifica SkipExternalResources = true. Adicione um fragmento restrito em WhitelistedResources somente quando uma referência confiável realmente precisar ser resolvida.

Comparando as Três

Preocupação Padrão Bloquear tudo Bloquear + lista de permissões
Propriedades a definir 0 1 2
Requisições externas todas as referências nenhuma apenas as da lista de permissões
Controle por referência não não sim
URLs mortas aumentam o tempo de carregamento sim não apenas as da lista de permissões
Melhor para documentos que você produziu documentos de qualquer outra origem modelos confiáveis entre conteúdo não confiável

A decisão segue a procedência do documento, não o desempenho. Documentos gerados pelos seus sistemas podem manter o padrão. Documentos externos justificam o bloqueio. Use a lista de permissões no ponto em que uma referência específica realmente precise ser resolvida – por exemplo, um modelo corporativo que puxa sua imagem de cabeçalho de uma URL interna, entre relatórios cujos autores colaram imagens de onde quiseram.

Os Dois Erros

Ambos produzem o mesmo sintoma: você define a opção e parece que nada acontece.

Uma lista de permissões sem o interruptor. WhitelistedResources é consultada apenas quando SkipExternalResources é true. Definida sozinha, ela não faz nada – não há bloqueio para que ela faça uma exceção. Se a lista de permissões parece ignorada, verifique isso primeiro.

Opções apenas na origem. Esta é a mais sutil. As opções de carregamento descrevem como um documento é carregado. O construtor Comparer recebe as opções para a origem; cada chamada a Add() recebe as opções para aquele alvo:

using (Comparer comparer = new Comparer(sourcePath, loadOptions))
{
    comparer.Add(targetPath, loadOptions);
    comparer.Compare(outputPath);
}

Passá‑las ao construtor e esquecer da chamada Add() protege apenas a origem, enquanto cada alvo ainda busca suas referências. A comparação tem sucesso, o resultado parece plausível, e metade dos seus documentos ainda está acessando a rede. Quando origem e alvo precisam de tratamentos diferentes, passe instâncias distintas de LoadOptions – esse é exatamente o motivo pelo qual a API as aceita por documento.

Verificando se Realmente Funcionou

Um recurso bloqueado deixa quase nenhum rastro. O documento de saída simplesmente não contém a imagem, o que se parece muito com um documento que nunca a teve. Ler o arquivo resultante, portanto, é um método pobre para confirmar que a configuração entrou em vigor.

Observe o lado do servidor em vez disso. O exemplo de referência adota essa abordagem deliberadamente: ele inicia um pequeno listener HTTP em uma porta de loopback livre, grava seus documentos de demonstração apontando para essa porta e registra cada requisição recebida, imprimindo a contagem por comparação. Cinco requisições, depois zero, depois três. Uma captura de tráfego contra suas fontes reais de documentos lhe dá a mesma confiança.

Conclusão

Três configurações, uma regra de decisão: deixe os documentos que você gerou manter o padrão, defina SkipExternalResources = true para todo o resto, e inclua um fragmento de URL restrito na lista de permissões apenas onde uma referência confiável ainda precise ser resolvida.

Então verifique as duas coisas que silenciosamente desfazem o trabalho – uma lista de permissões sem SkipExternalResources = true e opções passadas ao construtor Comparer mas não a cada chamada Add() – e confirme a partir do lado do servidor em vez de analisar o arquivo de saída.

Recursos Adicionais