Швидкий старт
Відобразіть ваш перший документ за кілька хвилин
Цей посібник переводить додаток ASP.NET Core від порожнього Program.cs до документу, відображеного в браузері: реєстрація сервера, повний пакет Viewer (панель інструментів Viewer, монтування Viewer та необов’язкові стрічки Пошуку/Анотації), посилання на ресурси, ініціалізація клієнта, відкриття документу та виконання.
Хост-додаток повинен бути націлений на середовище виконання, яке підтримує netstandard2.1 — .NET Core 3.0 або новіше. Додатки‑зразки, що постачаються з цим випуском, орієнтовані на net8.0.
Налаштування сервера
AddDoconut() реєструє сервіси; UseDoconutResources() і UseDoconut() підключають проміжне ПЗ. Виклик ресурсів має бути першим. Виклики сесії також потрібні — стандартна безпека документу Doconut перевіряє кожен запит сторінки проти стану сесії ASP.NET. Стан сесії потребує кешу: AddDistributedMemoryCache() його забезпечує. MVC або Razor Pages вже реєструють його, але мінімальний API‑додаток — ні, і без нього додаток зупиняється під час запуску з помилкою Unable to resolve service for type 'IDistributedCache'. Вже реєстрували Doconut під час Встановлення? Перейдіть до наступного розділу.
builder.Services.AddDoconut(options =>
{
options.LicensePath = "Doconut.Viewer.lic";
});
builder.Services.AddDistributedMemoryCache(); // стан сесії потребує кешу (MVC/Razor Pages вже додає його)
builder.Services.AddSession(); // Безпека документу Doconut працює на стані сесії ASP.NET
app.UseSession(); // викликайте UseSession() перед UseDoconut()
app.UseDoconutResources(); // повинно бути зареєстровано перед UseDoconut()
app.UseDoconut();Додаток‑зразок зберігає історичну форму запиту DocImage.axd та монтує проміжне ПЗ документу на відповідній гілці. Це налаштування слід скопіювати, коли ви переносите існуючий .NET Standard додаток, оскільки клієнт продовжує викликати той самий URL, який він завжди використовував:
builder.Services.AddDoconut(options =>
{
options.UnsafeMode = false; // зберегти включену перевірку сесії Doconut
options.ShowDoconutInfo = false;
});
builder.Services.AddDistributedMemoryCache(); // стан сесії потребує кешу (MVC/Razor Pages вже додає його)
builder.Services.AddSession(options =>
{
options.IdleTimeout = TimeSpan.FromSeconds(3600);
options.Cookie.HttpOnly = true;
options.Cookie.IsEssential = true;
});
// Потрібно для багатобайтових кодувань у деяких форматах документів.
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
app.UseStaticFiles();
app.UseRouting();
app.UseSession();
app.UseDoconutResources(); // ПЕРЕД гілкою документу
app.MapWhen(
context => context.Request.Path.Value?.EndsWith("DocImage.axd", StringComparison.OrdinalIgnoreCase) == true,
branch => branch.UseDoconut());UseDoconut() не приймає параметрів у цьому випуску — усе перенесено до AddDoconut(). MiddlewarePath — це координуюче значення; воно не створює гілку ASP.NET Core самостійно. ResourcesPath обслуговує вбудований пакет за адресою /doconut-res, а шлях до зображень віджету тому ResPath: 'doconut-res/images'.
Додайте переглядач на сторінку
Viewer є необхідним ядром сторінки. Його поверхня рендерингу використовує два вкладені div елементи:
<div id="divDocViewer">
<div id="div_ctlDoc"></div>
</div>Розглядайте панель інструментів, монтування модулів та поверхню Viewer як одну композицію сторінки. Пошук і Анотація вставляють свої вбудовані стрічки в необов’язкові монтування, проте ці модулі ніколи не працюють окремо: вони завжди приєднуються до Viewer на тій самій сторінці.
<nav id="toolbar" aria-label="Document viewer controls">
<!-- Навігація переглядача, масштабування, кнопки Пошуку та Анотації -->
</nav>
<div id="searchBarMount"></div>
<div id="annBarMount"></div>
<div id="divDocViewer">
<div id="div_ctlDoc"></div>
</div>Підключення ресурсів переглядача
У Razor‑виді, інжектований сервіс Viewer генерує <link> та <script> теги переглядача у правильному порядку залежностей — віджет є jQuery‑плагіном, тому jQuery має бути завантажений до скриптів переглядача:
@inject Doconut.Viewer Viewer
@Html.Raw(Viewer.ReferenceCss(new CssConfig
{
IncludeBootstrapCss = true,
IncludeViewerCss = true
}))
@Html.Raw(Viewer.ReferenceScripts(new ScriptConfig
{
IncludeJQuery = true,
IncludeBootstrap = true,
IncludeViewerScripts = true
}))Для повного пакету Viewer запитуйте ресурси Viewer та модулів разом:
@Html.Raw(Viewer.ReferenceCss(new CssConfig
{
IncludeBootstrapCss = true,
IncludeViewerCss = true,
IncludeSearchCss = true,
IncludeAnnotationCss = true
}))
@Html.Raw(Viewer.ReferenceScripts(new ScriptConfig
{
IncludeJQuery = true,
IncludeBootstrap = true,
IncludeViewerScripts = true,
IncludeSearchScripts = true,
IncludeSearchBar = true,
IncludeAnnotationScripts = true,
IncludeAnnotationBar = true
}))IncludeViewerCss і IncludeViewerScripts — обов’язкові базові прапорці. Ніколи не публікуйте приклад Search або Annotation Ribbon без них, без монтування Viewer та екземпляра docViewer. ReferenceCss і ReferenceScripts пропускають ресурси необов’язкового модуля, якщо поточна ліцензія не надає таку можливість; ядро Viewer все одно запускається.
Ці виклики замінюють вручну скопійовані файли скриптів. Якщо застосунок все ще завантажує власні docViewer.js, documentLinks.js або docViewer.UI.js, видаліть ці теги, коли сторінка працює з виданих ресурсів — дивіться посібник з міграції.
Ініціалізація переглядача
Клієнтський віджет — це jQuery‑плагін. Ось мінімальний набір реальних параметрів ініціалізації (не псевдокод):
let searchBar = null;
let annBar = null;
const objViewer = $('#div_ctlDoc').docViewer({
showThumbs: true,
autoLoad: false,
pageZoom: 100,
FitType: 'width',
BasePath: '/',
ResPath: 'doconut-res/images',
onViewerReady: function () {
// сторінки видимі; безпечно приховати індикатор завантаження тут
},
// Перенаправити події життєвого циклу анотації до вбудованої панелі, коли вона присутня.
onAnnLoaded: () => annBar?.handleAnnLoaded(),
onAnnSaved: () => annBar?.handleAnnSaved(),
onAnnSaveError: () => annBar?.handleAnnSaveError(),
onAnnClosed: () => annBar?.handleAnnClosed(),
onError: function (message) {
console.error('Doconut viewer error:', message);
}
});Два деталі вирішують, чи працюватиме ця сторінка взагалі:
- Зворотні виклики — це параметри.
onViewerReady,onViewerBusy,onCopyіonAutoLoadStatusпередаються в об’єкт параметрів. Попередній переглядач формував глобальні імена функцій зidконтейнера і викликав їх; цей цього не робить. Обробник, оголошений лише як глобальна функція, ніколи не виконується, і нічого не повідомляється. ResPathвказує на префікс ресурсів, а не на корінь застосунку. За замовчуваннямResourcesPathмає значенняdoconut-res/images. Якщо ви змінилиResourcesPath, використайте свій власний префікс.
Обидва ці випадки «тихо» не працюють — дивіться Усунення проблем і посібник з міграції.
Кейс параметрів справді змішаний — showThumbs, autoLoad і pageZoom написані у camelCase, а FitType, BasePath і ResPath — у PascalCase. Єдина послідовність відсутня; неправильний регістр призведе до того, що параметр буде проігноровано без повідомлення (віджет повернеться до значення за замовчуванням замість помилки).
Збірка повного пакету Viewer
Обидва референсні застосунки встановлюють наступні частини разом на одній сторінці:
| Частина пакету | Вимога | Як підключається |
|---|---|---|
Ресурси Viewer, монтування та objViewer | Обов’язково | Основний рендерер документів |
| Панель інструментів Viewer | Обов’язково у референсному складі | Розмітка хоста; кнопки викликають той самий objViewer |
| Панель пошуку | Опціональний, ліцензований модуль | doconutSearchBar(...).attach(objViewer) |
| Панель анотацій | Опціональний, ліцензований модуль | doconutAnnotationBar(...).attach(objViewer) |
Хоча головна панель інструментів Viewer є розміткою хоста, вона встановлюється разом з Viewer і ніколи не повинна документуватись як окремий контроль. Це зберігає її розташування, підписи, іконки та правила авторизації під контролем вашого застосунку, тоді як кожна кнопка керує тим самим екземпляром Viewer:
<nav id="toolbar" aria-label="Document viewer controls">
<button type="button" onclick="objViewer.GotoPage(1)">First</button>
<button type="button" onclick="objViewer.Next(false)">Previous</button>
<button type="button" onclick="objViewer.Next(true)">Next</button>
<button type="button" onclick="objViewer.GotoPage(objViewer.TotalPages())">Last</button>
<button type="button" onclick="objViewer.Zoom(false)">Zoom out</button>
<button type="button" onclick="objViewer.Zoom(true)">Zoom in</button>
<button type="button" onclick="objViewer.FitType('width')">Fit width</button>
<button type="button" onclick="objViewer.FitType('height')">Fit height</button>
<button type="button" id="openSearch">Search</button>
<button type="button" id="openAnnotations">Annotations</button>
</nav>Дотримуйтесь порядку ініціалізації пакету, який використовується в обох референсних застосунках:
- Вивести CSS для Viewer та ліцензованих модулів.
- Відобразити панель інструментів Viewer, монтування Search/Annotation та монтування Viewer разом.
- Вивести скрипти для Viewer та ліцензованих модулів.
- Завантажити власний скрипт‑помічник панелі інструментів хост‑додатку.
- Ініціалізувати
docViewerта зберегти отриманийobjViewer. - Ініціалізувати кожну ліцензовану панель Search або Annotation.
- Викликати
attach(objViewer)для кожної панелі. - Відкрити документ і зберегти його токен для запитів Viewer та модулів.
Серверні захисти важливі: коли необов’язкова можливість недоступна, її скрипт не виводиться, тому функція jQuery‑плагіна не існує.
<script>
let currentToken = '';
const refitViewer = () =>
requestAnimationFrame(() => objViewer.Refit());
@if (Viewer.IsSearchEnabled)
{
<text>
searchBar = $('#searchBarMount').doconutSearchBar({
docId: 'ctlDoc',
getRequestParams: () => ({ token: currentToken }),
onLayout: refitViewer
});
searchBar.attach(objViewer);
</text>
}
@if (Viewer.IsAnnotationEnabled)
{
<text>
annBar = $('#annBarMount').doconutAnnotationBar({
docId: 'ctlDoc',
getRequestParams: () => ({ token: currentToken }),
onLayout: refitViewer
});
annBar.attach(objViewer);
</text>
}
document.getElementById('openSearch').addEventListener('click', () => {
if (!searchBar) return;
searchBar.isOpen() ? searchBar.close() : searchBar.open();
});
document.getElementById('openAnnotations').addEventListener('click', () => {
if (!annBar) return;
annBar.isOpen() ? annBar.close() : annBar.open();
});
</script>Обидва вбудованих компоненти генерують власний DOM панелі. Search містить групи Find, Options і Results. Annotation містить інструменти створення, керування стилем, дії збереження та опціональні дії експорту/зображення. Панелі надають open(), close(), reset() і isOpen(); завжди викликайте attach(objViewer) один раз після їх створення.
У наведеному вище прикладі пропущено опціональні колбеки хоста та кінцеві точки експорту/зображення Annotation, щоб мінімізувати старт. Дивіться Пошук і Анотації для повного налаштування функціоналу, або Власні теми для стилізації або заміни панелі інструментів Viewer, якою керує хост.
Відкрити документ
На стороні сервера є один кінцевий пункт: впроваджений сервіс Viewer відкриває документ і повертає токен сесії. Відкриття є асинхронним у цьому випуску.
app.MapPost("/api/open", async (Viewer viewer) =>
{
// Токен є непрозорим — передайте його віджету, ніколи не журналюйте і не зберігайте його.
string token = await viewer.OpenDocumentAsync("wwwroot/files/Sample.pdf");
return Results.Ok(new { token });
});Клієнт отримує цей токен і передає його віджету за допомогою objViewer.View(token):
fetch('/api/open', { method: 'POST' })
.then(resp => resp.json())
.then(data => {
currentToken = data.token;
objViewer.View(currentToken);
});Немає нічого, що потрібно підготувати перед першим відкриттям. Якщо ви переносите застосунок далі, видаліть будь‑який виклик InitCache() та прапорець, що його захищає — метод більше не існує.
Закрити документ
Викликайте objViewer.Close(), коли користувач залишає переглядач або відкриває інший документ. У серверно‑орієнтованих робочих процесах viewer.CloseDocument(token) негайно видаляє кешовану сесію, звільняє рушій рендерингу, стирає його маркер безпеки та анулює токен. Поступове закінчення терміну дії зрештою виконує те ж очищення, проте явне закриття рекомендується для великих документів.
Завершений потік запитів виглядає так:
AddDoconut + middleware
-> render CSS/scripts and mount div
-> initialize docViewer
-> OpenDocumentAsync
-> return opaque token
-> objViewer.View(token)
-> page/search/annotation requests
-> Close / CloseDocumentСтавте токен як облікові дані типу bearer: ніколи не журналюйте його, не зберігайте, передавайте лише віджету. Він ідентифікує активну сесію документа на сервері і перестає працювати, коли ця сесія закінчується — повторно відкрийте документ, щоб отримати новий.
Запустити
Помістіть PDF у wwwroot/files/Sample.pdf, запустіть dotnet run і відкрийте сторінку, що містить віджет. Перша сторінка відображається у переглядачі, з панеллю мініатюр зліва. Якщо цього не відбувається, дивіться Усунення проблем.
Що ви отримуєте без ліцензії
Відсутність ліцензії не викликає помилок. Переглядач відображається звичайно, але кожна сторінка містить водяний знак оцінки. Дивіться Налаштування ліцензії, щоб дізнатися, як Doconut знаходить ліцензію і що змінюється після її виявлення.
Чи була ця сторінка корисною?