Pendahuluan

Seorang rekan mengirim dua revisi kontrak dan meminta Anda membandingkannya. Anda memasukkan keduanya ke layanan perbandingan, hasilnya kembali, dan semuanya tampak normal. Yang tidak Anda lihat adalah bahwa salah satu dokumen tersebut berisi gambar yang ditautkan ke URL, dan server Anda menghubungi host itu pada saat file dibuka. Tidak ada yang pada output yang memberi tahu Anda bahwa hal itu terjadi.

Ini bukan sebuah cacat – itulah yang dimaksud dengan memuat dokumen secara setia. Sebuah file OOXML dapat merujuk ke gambar yang berada di server web alih‑alih berada di dalam paket, dan baik Word maupun perpustakaan apa pun yang memuat dokumen dengan benar akan menyelesaikan referensi tersebut. GroupDocs.Comparison untuk .NET menyediakan dua properti pada LoadOptions yang memungkinkan Anda memutuskan apakah referensi tersebut diselesaikan: SkipExternalResources dan WhitelistedResources.

Kedua properti tersebut memberikan tiga konfigurasi, dan artikel ini membandingkan ketiganya – default yang permisif, memblokir semuanya, dan memblokir semuanya kecuali referensi yang dinamai. Pada akhir bacaan Anda akan tahu konfigurasi mana yang dipilih untuk sumber dokumen tertentu, serta dua kesalahan yang membuat pengaturan ini tampak tidak berfungsi.

💡 Contoh lengkap yang dapat dijalankan: blokir-sumber‑daya‑eksternal‑pada‑pemuat‑dokumen‑dotnet – sebuah proyek konsol yang melayani gambar yang direferensikan sendiri dan mencatat setiap permintaan, sehingga Anda dapat melihat setiap pengaturan beraksi.

Di Mana Referensi Eksternal Bersembunyi

Sebelum memilih sebuah pengaturan, penting untuk memahami apa yang Anda pilih. Sebuah file .docx menyimpan referensi eksternal di dua tempat yang berbeda, dan keduanya mudah terlewat karena tidak terlihat dalam teks dokumen.

Yang pertama adalah sebuah relationship di word/_rels/document.xml.rels yang memiliki TargetMode="External" dan sebuah URL absolut. Gambar muncul di badan dokumen sebagai sebuah drawing yang menunjuk ke relationship tersebut melalui ID, sehingga URL itu sendiri tidak pernah muncul di dekat konten yang dipengaruhinya.

Yang kedua adalah kode bidang INCLUDEPICTURE di dalam badan dokumen, yang menyimpan URL di dalam instruksi bidang. Word menyelesaikannya saat halaman dirender; sebuah perpustakaan perbandingan menyelesaikannya saat dokumen dimuat.

Kedua mekanisme menghormati dua opsi pemuatan yang dibahas di bawah, yang penting karena sebuah dokumen dapat menggunakan salah satu atau keduanya. Sebuah referensi yang Anda temukan di file relationships bukanlah bukti bahwa tidak ada referensi kedua di kode bidang.

Pendekatan 1: Default – Referensi Diselesaikan

SkipExternalResources secara default bernilai false, sehingga dokumen yang dimuat tanpa konfigurasi akan menyelesaikan referensi jaraknya:

LoadOptions loadOptions = new LoadOptions
{
    SkipExternalResources = false
};

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

Ini memberikan fidelitas tertinggi: dokumen yang dibandingkan berisi semua yang mereka referensikan, persis seperti yang akan dirender Word. Untuk dokumen yang dihasilkan oleh aplikasi atau templat Anda sendiri, di mana setiap URL referensi mengarah ke infrastruktur yang Anda kelola, ini adalah pilihan yang tepat – dan gambar yang tertaut hilang dapat membuat perbandingan menjadi menyesatkan.

Biayanya adalah setiap referensi dihubungi, siapa pun yang menambahkannya. Ada juga biaya waktu yang tidak berhubungan dengan kepercayaan: sebuah URL referensi yang tidak lagi dapat di‑resolve membuat proses pemuatan menunggu hingga percobaan koneksi selesai, pada setiap perbandingan.

Pendekatan 2: Blokir Semua Sumber Daya Eksternal

Satu properti mematikan penyelesaian referensi jarak jauh untuk dokumen tersebut:

LoadOptions loadOptions = new LoadOptions
{
    SkipExternalResources = true
};

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

Tidak ada permintaan yang dikirim. Gambar yang direferensikan tidak ada dalam hasil, dan – inilah bagian yang penting untuk dijelaskan – tidak ada hal lain yang berubah. Pengaturan ini mengatur apa yang dimuat, bukan bagaimana perbedaan ditemukan, sehingga perubahan tekstual dan struktural antara dua dokumen tetap terdeteksi persis seperti sebelumnya. Satu hal yang hilang adalah kemampuan mendeteksi perubahan di dalam gambar yang direferensikan, karena gambar tersebut tidak pernah dimuat.

Ini adalah konfigurasi yang dapat dijadikan baseline untuk dokumen yang tidak Anda buat: unggahan pengguna di aplikasi web, file yang diterima lewat email, apa pun yang dibandingkan pada agen build di mana permintaan keluar jarang diinginkan. Ini bersifat all‑or‑nothing, meskipun – sebuah gambar tertaut yang sebenarnya Anda inginkan akan diblokir bersama yang lainnya, dan hasilnya hanya tidak menyertakan gambar tersebut tanpa memberi tahu apa yang terjadi.

Pendekatan 3: Blokir Semua Kecuali Referensi yang Dinamai

Konfigurasi ketiga adalah yang paling menguntungkan bila Anda membaca dengan seksama. WhitelistedResources menerima List<string> dan hanya dipertimbangkan ketika SkipExternalResources bernilai 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);
}

Entri‑eninya adalah fragmen URL, bukan nama file. Setiap fragmen dicocokkan dengan URL referensi, dan kecocokan di mana saja dalam URL memperbolehkan sumber daya tersebut. Inilah yang membuat whitelist dapat dipindahkan: "includepicture-field.png" memperbolehkan gambar apa pun dengan skema, host, dan jalur apa pun yang mendahuluinya, sehingga daftar yang sama dapat dipakai di lingkungan pengembangan dan produksi tanpa diubah.

Properti yang sama dapat berbalik arah. Sebuah fragmen yang pendek atau generik – logo.png, atau lebih buruk lagi, .png – dapat cocok dengan referensi yang tidak pernah Anda maksudkan untuk diizinkan. Pilihlah fragmen yang cukup spesifik untuk mengidentifikasi satu sumber daya yang Anda maksud.

Pada contoh referensi, konfigurasi ini mengambil gambar yang masuk whitelist dan membiarkan gambar referensi kedua, yang tidak tercakup oleh entri apa pun, tetap diblokir. Log permintaan menunjukkan tiga permintaan, sedangkan default yang permisif menghasilkan lima, dan hanya menampilkan nama file yang masuk whitelist.

Konfigurasi Mana yang Harus Anda Gunakan?

Sesuaikan pengaturan dengan asal dokumen. Dokumen yang dihasilkan oleh aplikasi atau templat Anda dapat tetap menggunakan default, karena setiap URL referensi mengarah ke infrastruktur yang sudah Anda kelola. Apa pun yang datang dari luar – unggahan pengguna, lampiran email, file pihak ketiga – sebaiknya menggunakan SkipExternalResources = true. Tambahkan fragmen WhitelistedResources yang sempit hanya ketika satu referensi tepercaya memang harus diselesaikan.

Membandingkan Ketiganya

Aspek Default Blokir Semua Blokir + Whitelist
Properti yang diatur 0 1 2
Permintaan keluar semua referensi tidak ada hanya yang masuk whitelist
Kontrol per‑referensi tidak tidak ya
URL mati menambah waktu muat ya tidak hanya yang masuk whitelist
Cocok untuk dokumen yang Anda buat dokumen dari sumber lain templat tepercaya di antara konten tidak tepercaya

Keputusan didasarkan pada asal dokumen, bukan pada performa. Dokumen yang dihasilkan sistem Anda dapat tetap memakai default. Dokumen dari luar sebaiknya diblokir. Gunakan whitelist pada titik di mana satu referensi spesifik memang harus diselesaikan – misalnya templat korporat yang mengambil gambar header dari URL internal, di antara laporan yang penulisnya menempelkan gambar dari mana saja.

Dua Kesalahan

Kedua kesalahan ini menghasilkan gejala yang sama: Anda mengatur opsi, tetapi tampaknya tidak berpengaruh.

Whitelist tanpa mengaktifkan switch. WhitelistedResources hanya dipertimbangkan ketika SkipExternalResources bernilai true. Jika hanya mengatur whitelist, ia tidak melakukan apa‑apa – tidak ada pemblokiran yang dapat dikecualikan. Jika whitelist tampak diabaikan, periksa hal ini terlebih dahulu.

Opsi hanya pada sumber. Ini yang lebih halus. Opsi pemuatan menjelaskan bagaimana satu dokumen dimuat. Konstruktor Comparer menerima opsi untuk sumber; setiap pemanggilan Add() menerima opsi untuk target tersebut:

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

Jika Anda hanya melewatkannya ke konstruktor dan melupakan pemanggilan Add(), maka sumber terlindungi sementara setiap target tetap mengambil referensinya. Perbandingan berhasil, hasilnya tampak masuk akal, dan setengah dokumen Anda masih menghubungi jaringan. Bila sumber dan target memerlukan penanganan berbeda, berikan instance LoadOptions yang terpisah – itulah mengapa API menerima opsi per dokumen.

Memverifikasi Bahwa Pengaturan Benar‑benar Berfungsi

Sumber daya yang diblokir hampir tidak meninggalkan jejak. Dokumen output kehilangan gambar, yang tampak seperti dokumen yang memang tidak pernah memiliki gambar. Membaca file hasil bukanlah cara yang baik untuk memastikan pengaturan telah diterapkan.

Pantau sisi penyajian saja. Contoh referensi sengaja mengambil pendekatan ini: ia memulai listener HTTP kecil pada port loopback yang bebas, menulis dokumen demo yang menunjuk ke port tersebut, dan mencatat setiap permintaan yang diterima, menampilkan jumlah per perbandingan. Lima permintaan, kemudian nol, kemudian tiga. Jejak jaringan terhadap sumber dokumen nyata Anda memberi tingkat keyakinan yang sama.

Kesimpulan

Tiga konfigurasi, satu aturan keputusan: biarkan dokumen yang Anda hasilkan tetap memakai default, tetapkan SkipExternalResources = true untuk segala hal lainnya, dan whitelist fragmen URL yang sempit hanya ketika referensi tepercaya tertentu masih perlu diselesaikan.

Kemudian periksa dua hal yang secara diam‑diam membatalkan kerja – whitelist tanpa SkipExternalResources = true, dan opsi yang diberikan ke konstruktor Comparer tetapi tidak ke setiap pemanggilan Add() – serta verifikasi dari sisi penyajian, bukan dari file output.

Sumber Daya Tambahan