Быстрый старт
Отобразите первый документ за несколько минут
Этот пошаговый гид переводит приложение ASP.NET Core от пустого Program.cs к документу, отображаемому в браузере: регистрация сервера, полный пакет Viewer (панель инструментов Viewer, монтирование Viewer и необязательные ленты Поиска/Аннотации), ссылки на ресурсы, инициализация клиента, открытие документа и выполнение.
Настройка сервера
AddDoconut() регистрирует сервисы; UseDoconutResources() и UseDoconut() подключают middleware. Вызов ресурсов должен быть первым. Вызовы сессии также обязательны — безопасность документов Doconut проверяет каждый запрос страницы против состояния сессии ASP.NET. Состоянию сессии нужен кэш: AddDistributedMemoryCache() его предоставляет. Приложение MVC или Razor Pages уже регистрирует его, но минимальное API‑приложение — нет, и без него приложение останавливается при запуске с ошибкой Unable to resolve service for type 'IDistributedCache'. Уже регистрировали Doconut во время Installation? Перейдите к следующему разделу.
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();Для продакшн‑стиля расположения путей сопоставьте middleware документов с явной веткой и синхронно задайте четыре параметра пути:
builder.Services.AddDoconut(options =>
{
options.LicensePath = Path.Combine(AppContext.BaseDirectory, "wwwroot", "Doconut.Viewer.lic");
options.MiddlewarePath = "/doconut";
options.ResourcesPath = "/doconut-res";
options.UnsafeMode = false;
});
builder.Services.AddDistributedMemoryCache();
builder.Services.AddSession();
app.UseRouting();
app.UseSession();
app.UseDoconutResources();
app.Map("/doconut", branch => branch.UseDoconut());MiddlewarePath — это координационное значение; оно само по себе не сопоставляет ветку ASP.NET Core. В этом примере хост сопоставляет /doconut, поэтому клиент использует BasePath: 'doconut'. ResourcesPath обслуживает встроенный пакет по адресу /doconut-res, а путь к изображениям виджета тогда ResPath: 'doconut-res/images'. Записывайте оба без начального слеша: просмотрщик добавит его сам, иначе '/doconut' превратится в //doconut/DocImage.axd, который браузер воспримет как запрос к хосту с именем doconut, и просмотрщик завершит работу с ошибкой «The viewer handler could not be reached».
Добавление просмотрщика на страницу
Viewer — обязательное ядро страницы. Его поверхность рендеринга использует два вложенных div:
<div id="divDocViewer">
<div id="div_ctlDoc"></div>
</div>Относитесь к панели инструментов, монтированиям модулей и поверхности Viewer как к единой композиции страницы. Поиск и Аннотация внедряют свои встроенные ленты в необязательные монтирования, но эти модули никогда не работают отдельно: они всегда присоединяются к Viewer на той же странице. Используйте тот же порядок, что и в Doconut.TestApp и Doconut.TestApp.Distributed:
<nav id="toolbar" aria-label="Document viewer controls">
<!-- Viewer navigation, zoom, Search, and Annotation buttons -->
</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 — обязательные базовые флаги. Никогда не публикуйте пример ленты Поиска или Аннотации без них, без монтирования Viewer и экземпляра docViewer. ReferenceCss и ReferenceScripts опускают ресурсы необязательного модуля, если текущая лицензия не предоставляет эту возможность; ядро Viewer при этом всё равно запускается.
Инициализация просмотрщика
Клиентский виджет — это jQuery‑плагин. Ниже приведён минимальный набор реальных параметров инициализации (не псевдокод):
let searchBar = null;
let annBar = null;
const objViewer = $('#div_ctlDoc').docViewer({
showThumbs: true,
autoLoad: false,
pageZoom: 100,
FitType: 'width',
BasePath: 'doconut', // без начального '/'
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);
}
});Регистры параметров действительно смешанные — showThumbs, autoLoad и pageZoom записаны в camelCase, а FitType, BasePath и ResPath — в PascalCase. Нет единого правила; если указать неверный регистр, параметр будет тихо проигнорирован (виджет вернётся к значению по умолчанию вместо выброса ошибки).
Сборка полного пакета Viewer
Обе справочные приложения .NET 6 устанавливают следующие части вместе на одной странице:
| Часть пакета | Требование | Как подключается |
|---|---|---|
Ресурсы 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>Полная справочная панель также копирует wwwroot/js/viewerToolbar.js в приложение‑хост для функций вращения, миниатюр, печати, полноэкранного режима, смены макета и управления состоянием кнопок. Подключайте этот файл хоста после Viewer.ReferenceScripts(...). Держите вспомогательный скрипт и его разметку <nav id="toolbar"> вместе при копировании полной демонстрационной реализации.
Соблюдайте порядок инициализации пакета, используемый в обоих справочных приложениях:
- Вывести CSS для Viewer и лицензируемых модулей.
- Отрисовать панель инструментов Viewer, монтировать Search/Annotation и сам Viewer.
- Вывести скрипты для Viewer и лицензируемых модулей.
- Загрузить
viewerToolbar.jsиз приложения‑хоста. - Инициализировать
docViewerи сохранить полученныйobjViewer. - Инициализировать каждую лицензируемую ленту поиска или аннотаций.
- Вызвать
attach(objViewer)у каждой ленты. - Открыть документ и сохранить его токен для запросов Viewer и модулей.
Doconut.TestApp.Distributed сохраняет эту точную композицию UI и тот же вспомогательный скрипт панели. Его дополнительное значение запроса access и настройки повторных асинхронных рендеров относятся к распределённому транспорту; они не меняют способ сборки 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‑элемент ленты. Лента поиска содержит группы «Find», «Options» и «Results». Лента аннотаций включает инструменты авторинга, элементы управления стилем, действия сохранения и необязательные действия экспорта/изображения. Бар‑элементы предоставляют методы open(), close(), reset() и isOpen(); всегда вызывайте attach(objViewer) один раз после их создания.
В приведённом выше примере опущены необязательные обратные вызовы хоста и конечные точки экспорта/изображения аннотаций, чтобы минимизировать старт. См. Поиск и Аннотации для полного описания настройки функций, либо Пользовательские темы для стилизации или замены панели инструментов 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);
});Закрыть документ
Вызовите 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Относитесь к токену как к удостоверению доступа: никогда не логируйте его, не сохраняйте, передавайте только виджету. Он идентифицирует активный сеанс документа на сервере и перестаёт работать, когда сеанс истекает — откройте документ заново, чтобы получить новый токен.
Запустить
Поместите PDF в wwwroot/files/Sample.pdf, запустите dotnet run и откройте страницу, где размещён виджет. Первая страница отображается во просмотрщике с панелью миниатюр слева. Если этого не происходит, см. раздел Устранение неполадок.
Что вы получаете без лицензии
Отсутствие лицензии не вызывает ошибку. Просмотрщик работает нормально, но каждая страница содержит водяной знак оценки. См. раздел Настройка лицензии, чтобы узнать, как Doconut находит лицензию и какие изменения происходят после её обнаружения.
Была ли эта страница полезной?