💡 Повний робочий приклад доступний на GitHub:
compare-encrypted-pdf-and-word-documents-dotnet
Старий спосіб був болючим
Два варіанти договору про постачання потрапляють у вашу поштову скриньку. Обидва захищені паролем, кожен — іншим паролем, і комусь потрібна позначена копія, що показує, що змінилося. Бібліотека порівняння, яку ви використовуєте, очікує вхідні дані у вигляді відкритого тексту, тому в конвеєр додається крок: розшифрувати обидва файли у тимчасову папку, порівняти відкриті копії, а потім не забути їх видалити. Ця тимчасова папка стає найслабшою ланкою в процесі, який існує саме тому, що документи конфіденційні.
Існує ще один варіант тієї ж проблеми, який легше пропустити. Деякі команди уникають тимчасової папки і розшифровують у пам’яті, що вирішує питання прибирання, але не питання формату: API розшифрування різниться залежно від формату, тому підтримка зашифрованих електронних таблиць після зашифрованих PDF означає друге інтегрування, а не просто другий рядок коду.
Вартість полягає не стільки в виклику розшифрування — а в усьому, що навколо нього. Відкриті копії треба кудись записати, очистити їх у кожному шляху виходу, включаючи шляхи з помилками, і тримати їх подалі від резервних копій і дампів аварій. Диференціал, створений таким чином, за замовчуванням також не захищений, тому вихід з двох зашифрованих вхідних файлів стає єдиним файлом у ланцюжку, який будь-хто може відкрити.
Справжня вартість обхідного шляху розшифрування: тимчасова директорія, що містить відкриті копії документів, які були зашифровані з певної причини, з прибиранням, яке має бути правильним у кожному шляху помилки.
Є кращий спосіб
Порівняння захищених паролем документів — це можливість GroupDocs.Comparison для .NET, яка відкриває зашифровані PDF, DOCX, XLSX і PPTX файли «на місці» і вирішує, який пароль захищає результат порівняння. Ніякого кроку розшифрування, жодних відкритих проміжних файлів: пароль подається разом із документом у саме порівняння, як властивість у LoadOptions.
Перш ніж почати, вам знадобиться:
- .NET 8.0 SDK або новіша версія
- GroupDocs.Comparison 26.9.0 (temporary licence)
- Два зашифрованих документа одного формату та їх паролі
Встановіть за одну команду:
dotnet add package GroupDocs.Comparison
Новий спосіб: зашифровані документи безпосередньо в Comparer
У наведеному прикладі порівнюються два зашифрованих PDF — у джерела пароль 1234, у цілі — 4321 — і створюється один файл‑результат зі змінами, впровадженими в рядок. Навмисно різні паролі, бо саме там ховається перша помилка.
Крок 1 — Дайте кожному документу власний LoadOptions
Comparer тримає один джерело і будь‑яку кількість цілей, і кожен документ несе власний захист. Пароль джерела передається конструктору; пароль кожної цілі — у власному виклику Add.
// One LoadOptions per document - the constructor's options unlock the
// source only, and never reach the targets.
using var comparer = new Comparer("source.pdf",
new LoadOptions { Password = "1234" });
comparer.Add("target.pdf", new LoadOptions { Password = "4321" });
Саме це часто збиває людей з пантелику. Передача одного LoadOptions у конструктор і очікування, що він охопить цілі, — найпоширеніший спосіб помилки, і через те, як вона проявляється, вона не повідомляє про себе там, де ви її шукаєте.
Крок 2 — Визначте, що захищає результат
CompareOptions.PasswordSaveOption вибирає захист вихідного файлу: None, Source, Target або User. За замовчуванням — None, що тихо перетворює два зашифрованих вхідних файли в один незахищений результат.
// Inline markup, and the result reuses the source document's password.
var options = new PdfCompareOptions
{
DisplayMode = PdfCompareOptions.ComparisonDisplayMode.Inline,
PasswordSaveOption = PasswordSaveOption.Source
};
comparer.Compare("Result/1-pdf-inline.pdf", options);
Ключові моменти:
- PasswordSaveOption:
Sourceповторно використовує пароль джерела у вихідному файлі. ВиберітьUserразом ізSaveOptions.Password, щоб задати новий пароль. - ComparisonDisplayMode: вкладений у
PdfCompareOptions, який також пропонуєSideBySideіInterleaved.WordCompareOptionsоголошує власний перелік з тим же ім’ям, але іншими значеннями, тому «голе» ім’я не скомпілюється — використовуйте повну кваліфікацію.
Крок 3 — Захистіть вихідний файл власним паролем
Коли диференціал надходить до рецензентів, які не повинні мати жодного з оригінальних паролів, PasswordSaveOption.User бере значення з SaveOptions.Password замість повторного використання вхідного пароля.
var compareOptions = new PdfCompareOptions
{
DisplayMode = PdfCompareOptions.ComparisonDisplayMode.Inline,
PasswordSaveOption = PasswordSaveOption.User
};
var saveOptions = new SaveOptions { Password = "5678" };
comparer.Compare("Result/4-own-password.pdf", saveOptions, compareOptions);
Обидва об’єкти передаються у трьохаргументний перегруз Compare. Окреме встановлення SaveOptions.Password нічого не змінює — активує пароль лише значення перерахування. Результат цього виклику відкривається паролем 5678 і відхиляє 1234.
Чому мій try/catch навколо Comparer не ловить неправильний пароль?
Тому що конструктор ніколи не відкриває документ. Він лише записує шлях, і так само робить Add. Обидва документи читаються під час виконання Compare, і саме там викидається PasswordProtectedFileException з повідомленням Password is missing. Неправильний пароль поводиться ідентично: його приймають без повідомлення під час створення, а потім відхиляють під час Compare.
Тому обгорніть виклик порівняння, а не конструктор. Я виявив це «повільно», обгорнувши створення у try і спостерігаючи, як зашифрований файл проходить без проблем, а потім падає через три рядки. Репозиторій виводить кожен етап, що робить порядок очевидним при першому читанні:
using var comparer = new Comparer("source.pdf"); // succeeds
comparer.Add("target.pdf"); // succeeds
comparer.Compare("Result/unreachable.pdf"); // throws here
Порівняння «поруч»: до і після
| До (спочатку розшифрувати) | Після (GroupDocs.Comparison) | |
|---|---|---|
| Кроки конвеєра | Розшифрувати обидва, порівняти, видалити тимчасові копії | Порівняти |
| Відкритий текст на диску | Дві копії, прибирання у кожному шляху помилки | Жодних |
| Захист результату | Окремий крок повторного шифрування | Одне значення PasswordSaveOption |
| Покриття форматів | Інструменти розшифрування для кожного формату | Один LoadOptions.Password для PDF, DOCX, XLSX, PPTX |
| Необхідний код | Допоміжний розшифрувальник + порівняння | 4 рядки |
Функції порівняння не змінюються при зашифрованому вводі. Режими відображення, сторінки‑резюме та визначення стилю працюють точно так само, як і для відкритих файлів, бо захист обробляється повністю на рівні завантаження.
Саме це шарування робить покриття форматів дешевим. LoadOptions.Password — це просте властивість типу string, і саме воно розблоковує PDF, DOCX, XLSX і PPTX — код завантаження у прикладі для Word нижче є по‑буквенному копією того, що використовується у прикладах для PDF. Єдине, що змінюється, — це клас параметрів, і лише тому, що кожен формат пропонує різні варіанти рендерингу. Додавання підтримки зашифрованих електронних таблиць до коду, який вже порівнює зашифровані PDF, не вимагає жодних змін у шляху завантаження.
Реальний приклад: редагування договору між юридичними фірмами
Юридична команда отримує кожну редакцію договору зашифрованою, причому пароль змінюється при кожному обміні, щоб випадкове розкриття пароля не розкрило всю історію. Партнер‑рецензент потребує одну позначену копію на кожен раунд, і згідно з правилами зберігання позначена копія не може залишатися незахищеною у файловому сховищі.
Два налаштування покривають це. Кожен документ розблоковується своїм LoadOptions, тому обертання паролів не потребує спеціальної обробки, а PasswordSaveOption.User дає кожному розповсюдженому диференціалу власний пароль — той, що відкриває саме порівняння і нічого більше.
// Word revisions, so the reviewing partner can accept or reject each edit.
var options = new WordCompareOptions
{
DisplayMode = WordCompareOptions.ComparisonDisplayMode.Revisions,
PasswordSaveOption = PasswordSaveOption.Source
};
using var comparer = new Comparer("round3.docx",
new LoadOptions { Password = "1234" });
comparer.Add("round4.docx", new LoadOptions { Password = "4321" });
comparer.Compare("Result/redline.docx", options);
Що ще можна робити з GroupDocs.Comparison?
- Порівнювати більше двох захищених документів: додати кілька зашифрованих цілей до одного порівняння, для форматів Word і презентацій.
- Створювати нативні ревізії Word:
WordCompareOptions.ComparisonDisplayMode.Revisionsзаписує зміни, які рецензент може прийняти або відхилити безпосередньо у Word. - Керувати завантаженням зовнішніх ресурсів: блокувати або в білому списку віддалені посилання, які містить документ, інша захисна функція
LoadOptions. - Генерувати сторінку‑резюме:
GenerateSummaryPageдодає огляд змін до документу‑результату.
Висновок
Обхідний шлях розшифрування ніколи не стосувався самого порівняння — він був наслідком бібліотеки, яка не могла прочитати ваші файли. Встановлення LoadOptions.Password для кожного документа усуває тимчасову папку, шляхи прибирання та незахищений диференціал у кінці ланцюжка. Залишаються лише три рішення: пароль для кожного документа, явний PasswordSaveOption замість типового None і обробка помилок навколо Compare, де фактично виникає збій.
Готові автоматизувати ваш документообіг?
- Try the free API trial
- Explore loading password-protected documents
- Read the full comparison guide for protected documents
- Check out the sample project on GitHub