Giới thiệu

Một đồng nghiệp gửi hai phiên bản của một hợp đồng và yêu cầu bạn so sánh chúng. Bạn đưa cả hai vào dịch vụ so sánh, kết quả trả về và mọi thứ trông bình thường. Điều bạn không thấy là một trong những tài liệu chứa một hình ảnh liên kết tới một URL, và máy chủ của bạn đã liên lạc với máy chủ đó ngay khi tệp được mở. Không có gì trong đầu ra cho biết điều này đã xảy ra.

Đây không phải là lỗi – đó là cách mà việc tải tài liệu một cách trung thực hoạt động. Một tệp OOXML có thể tham chiếu một hình ảnh nằm trên máy chủ web thay vì bên trong gói, và cả Word và bất kỳ thư viện nào tải tài liệu đúng cách đều sẽ giải quyết tham chiếu đó. GroupDocs.Comparison cho .NET cung cấp hai thuộc tính trên LoadOptions cho phép bạn quyết định liệu nó có thực hiện việc này hay không: SkipExternalResources và WhitelistedResources.

Hai thuộc tính này tạo ra ba cấu hình, và bài viết này so sánh cả ba – mặc định cho phép, chặn mọi thứ, và chặn mọi thứ ngoại trừ các tham chiếu được đặt tên. Khi đọc xong, bạn sẽ biết nên chọn cấu hình nào cho nguồn tài liệu cụ thể, và hai sai lầm khiến các thiết lập này có vẻ không hoạt động.

💡 Ví dụ đầy đủ hoạt động: block-external-resources-on-document-load-dotnet – một dự án console có thể chạy được, tự phục vụ các hình ảnh được tham chiếu và ghi lại mọi yêu cầu, để bạn có thể quan sát mỗi thiết lập có hiệu lực như thế nào.

Nơi các Tham chiếu Ngoại vi Ẩn Nấp

Trước khi chọn một thiết lập, bạn cần hiểu những gì mình đang lựa chọn. Một tệp .docx chứa các tham chiếu ngoại vi ở hai vị trí riêng biệt, và chúng dễ bị bỏ qua vì không hiển thị trong nội dung tài liệu.

Thứ nhất là một relationship trong word/_rels/document.xml.rels có TargetMode="External" và một URL tuyệt đối. Hình ảnh xuất hiện trong thân tài liệu dưới dạng một drawing trỏ tới relationship bằng ID, vì vậy URL thực tế không xuất hiện gần nội dung mà nó ảnh hưởng.

Thứ hai là một field code INCLUDEPICTURE trong thân tài liệu, chứa URL bên trong một chỉ thị trường. Word giải quyết nó khi trang được hiển thị; một thư viện so sánh giải quyết nó khi tài liệu được tải.

Cả hai cơ chế đều tôn trọng hai tùy chọn tải được thảo luận dưới đây, điều này quan trọng vì một tài liệu có thể sử dụng một hoặc cả hai. Một tham chiếu bạn thấy trong tệp relationships không có nghĩa là không có tham chiếu thứ hai trong field code.

Cách Tiếp Cận 1: Mặc Định – Giải Quyết Tham Chiếu

SkipExternalResources mặc định là false, vì vậy một tài liệu được tải mà không có cấu hình sẽ giải quyết các tham chiếu từ xa:

LoadOptions loadOptions = new LoadOptions
{
    SkipExternalResources = false
};

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

Điều này mang lại độ trung thực cao nhất: các tài liệu được so sánh chứa mọi thứ chúng tham chiếu, chính xác như Word sẽ hiển thị. Đối với các tài liệu do ứng dụng hoặc mẫu của bạn tạo ra, nơi mọi URL tham chiếu đều trỏ tới hạ tầng bạn quản lý, đây là lựa chọn đúng – và một hình ảnh liên kết bị thiếu có thể làm cho việc so sánh trở nên gây hiểu lầm.

Chi phí là mọi tham chiếu đều bị liên lạc, bất kể ai đã đặt chúng. Ngoài ra còn có chi phí thời gian mà không liên quan đến độ tin cậy: một URL tham chiếu không còn tồn tại sẽ khiến quá trình tải phải chờ hết thời gian cố gắng kết nối, cho mỗi lần so sánh.

Cách Tiếp Cận 2: Chặn Mọi Tài Nguyên Ngoại Vi

Một thuộc tính tắt việc giải quyết tham chiếu từ xa cho tài liệu đó:

LoadOptions loadOptions = new LoadOptions
{
    SkipExternalResources = true
};

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

Không có yêu cầu nào được gửi. Các hình ảnh được tham chiếu sẽ không xuất hiện trong kết quả, và – đây là phần cần làm rõ – không có gì khác thay đổi. Thiết lập này chỉ quyết định những gì được tải, không ảnh hưởng đến cách phát hiện sự khác biệt, vì vậy các thay đổi về văn bản và cấu trúc giữa hai tài liệu vẫn được phát hiện như trước. Điều duy nhất bạn mất là khả năng phát hiện thay đổi bên trong một hình ảnh đã được tham chiếu, vì hình ảnh đó chưa bao giờ được tải.

Đây là cấu hình nên được coi là chuẩn cho các tài liệu bạn không tạo ra: tải lên của người dùng trong một ứng dụng web, tệp nhận được qua email, bất kỳ tài liệu nào được so sánh trên một máy build nơi các yêu cầu ra ngoài hiếm khi được mong muốn. Nó là “tất cả hoặc không gì”, tuy nhiên – một hình ảnh liên kết mà bạn thực sự muốn sẽ bị chặn cùng với phần còn lại, và kết quả sẽ thiếu hình ảnh đó mà không có thông báo nào.

Cách Tiếp Cận 3: Chặn Mọi Thứ Ngoại Trừ Các Tham Chiếu Được Đặt Tên

Cấu hình thứ ba là cấu hình cho phép bạn đọc kỹ hơn. WhitelistedResources nhận một List<string> và chỉ được tham chiếu khi SkipExternalResources là 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);
}

Các mục trong danh sách là đoạn URL, không phải tên tệp. Mỗi mục được so sánh với URL tham chiếu, và một khớp ở bất kỳ vị trí nào trong URL đều cho phép tài nguyên đó. Đó là lý do danh sách trắng (whitelist) có thể di chuyển được: "includepicture-field.png" cho phép hình ảnh bất kể scheme, host và path nào đứng trước, vì vậy cùng một danh sách hoạt động trong môi trường phát triển và sản xuất mà không cần chỉnh sửa.

Thuộc tính này cũng có thể gây rủi ro theo hướng ngược lại. Một đoạn ngắn hoặc chung chung – logo.png, hoặc tệ hơn, .png – có thể khớp với các tham chiếu bạn không muốn cho phép. Hãy chọn một đoạn đủ cụ thể để xác định duy nhất tài nguyên bạn muốn.

Trong mẫu tham chiếu, cấu hình này sẽ tải hình ảnh được đưa vào whitelist và để lại hình ảnh tham chiếu thứ hai, không có mục nào trong whitelist, bị chặn. Nhật ký yêu cầu sẽ hiển thị ba yêu cầu, trong khi mặc định cho phép sẽ tạo ra năm, và chỉ ghi tên tệp được whitelist.

Cấu Hình Nào Nên Sử Dụng?

Hãy khớp thiết lập với nguồn gốc của tài liệu. Các tài liệu do ứng dụng hoặc mẫu của bạn tạo ra có thể giữ mặc định, vì mọi URL tham chiếu đều trỏ tới hạ tầng bạn đã vận hành. Bất kỳ tài liệu nào đến từ bên ngoài – tải lên của người dùng, tệp đính kèm email, tệp của bên thứ ba – đều nên dùng SkipExternalResources = true. Chỉ thêm một đoạn whitelist hẹp khi một tham chiếu đáng tin cậy thực sự cần được giải quyết.

So Sánh Ba Cấu Hình

Mối quan tâm Mặc định Chặn tất cả Chặn + whitelist
Thuộc tính cần đặt 0 1 2
Yêu cầu ra ngoài tất cả các tham chiếu không có chỉ các tham chiếu trong whitelist
Kiểm soát theo tham chiếu không không có
URL chết làm tăng thời gian tải có không chỉ các URL trong whitelist
Thích hợp cho tài liệu do bạn tạo tài liệu từ nguồn bên ngoài mẫu tin cậy trong nội dung không tin cậy

Quyết định dựa trên nguồn gốc tài liệu hơn là hiệu năng. Các tài liệu do hệ thống của bạn tạo ra có thể giữ mặc định. Các tài liệu từ bên ngoài nên bị chặn. Whitelist chỉ được dùng khi một tham chiếu cụ thể thực sự cần được giải quyết – ví dụ, một mẫu công ty lấy hình tiêu đề từ một URL nội bộ, trong khi các báo cáo khác có thể chèn hình ảnh từ bất kỳ nơi nào.

Hai Sai Lầm

Cả hai đều gây ra cùng một triệu chứng: bạn đã đặt tùy chọn, nhưng nó dường như không hoạt động.

Whitelist mà không bật công tắc. WhitelistedResources chỉ được tham chiếu khi SkipExternalResources là true. Nếu chỉ đặt whitelist mà không bật SkipExternalResources, nó sẽ không làm gì cả – không có gì bị chặn để whitelist có thể tạo ngoại lệ. Nếu whitelist dường như bị bỏ qua, hãy kiểm tra điều này trước.

Tùy chọn chỉ áp dụng cho nguồn. Đây là lỗi tinh vi hơn. Các tùy chọn tải mô tả cách một tài liệu được tải. Hàm khởi tạo Comparer nhận các tùy chọn cho tài liệu nguồn; mỗi lời gọi Add() nhận các tùy chọn cho tài liệu đích:

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

Nếu bạn chỉ truyền chúng vào hàm khởi tạo và quên truyền vào Add(), nguồn sẽ được bảo vệ trong khi mọi đích vẫn sẽ tải các tham chiếu của chúng. Việc so sánh sẽ thành công, kết quả trông hợp lý, nhưng một nửa các tài liệu vẫn đang kết nối ra mạng. Khi nguồn và đích cần xử lý khác nhau, hãy truyền các thể hiện LoadOptions riêng biệt – đó chính là lý do API yêu cầu chúng cho mỗi tài liệu.

Xác Minh Rằng Nó Thực Sự Hoạt Động

Một tài nguyên bị chặn hầu như không để lại dấu vết. Tài liệu đầu ra thiếu một hình ảnh, trông giống như một tài liệu chưa bao giờ có hình ảnh. Vì vậy, đọc file kết quả không phải là cách tốt để xác nhận thiết lập đã có hiệu lực.

Hãy quan sát phía máy chủ. Mẫu tham chiếu thực hiện cách tiếp cận này một cách có chủ đích: nó khởi động một HTTP listener nhỏ trên một cổng loopback trống, ghi các tài liệu demo trỏ tới cổng đó, và ghi lại mọi yêu cầu nhận được, in số lượng yêu cầu cho mỗi lần so sánh. Năm yêu cầu, sau đó không có, rồi ba. Một bản ghi mạng đối với các nguồn tài liệu thực tế của bạn sẽ cho cùng mức độ tin cậy.

Kết Luận

Ba cấu hình, một quy tắc quyết định: để các tài liệu bạn tạo giữ mặc định, đặt SkipExternalResources = true cho mọi thứ còn lại, và whitelist một đoạn URL hẹp chỉ khi một tham chiếu đáng tin cậy cụ thể vẫn cần được giải quyết.

Sau đó, kiểm tra hai yếu tố có thể vô hiệu hoá công việc một cách im lặng – whitelist mà không bật SkipExternalResources = true, và các tùy chọn được truyền vào hàm khởi tạo Comparer nhưng không truyền vào mỗi lời gọi Add() – và xác minh từ phía máy chủ thay vì từ file đầu ra.

Tài Nguyên Bổ Sung