Быстрый старт

Отобразите первый документ за несколько минут

Этот пошаговый гид переводит приложение ASP.NET Core от пустого Program.cs к документу, отображаемому в браузере: регистрация сервера, полный пакет Viewer (панель инструментов Viewer, монтирование Viewer и опциональные ленты Поиска/Аннотаций), ссылки на ресурсы, инициализация клиента, открытие документа и выполнение.

Настройка сервера

AddDoconut() регистрирует сервисы; UseDoconutResources() и UseDoconut() подключают промежуточное ПО. Вызов ресурсов должен быть первым. Вызовы сессии также обязательны — стандартная безопасность документов Doconut проверяет каждый запрос страницы против состояния сессии ASP.NET. Уже регистрировали Doconut во время Установка? Перейдите к следующему разделу.

csharp
builder.Services.AddDoconut(options =>
{
    options.LicensePath = "Doconut.Viewer.lic";
});
builder.Services.AddSession(); // Doconut document security rides on ASP.NET session state

app.UseSession();          // call UseSession() before UseDoconut()
app.UseDoconutResources(); // must be registered before UseDoconut()
app.UseDoconut();

Для продакшн‑стиля расположения путей сопоставьте промежуточное ПО документов с явной веткой и держите четыре настройки путей согласованными:

csharp
builder.Services.AddDoconut(options =>
{
    options.LicensePath = Path.Combine(AppContext.BaseDirectory, "Doconut.Viewer.lic");
    options.MiddlewarePath = "/doconut";
    options.ResourcesPath = "/doconut-res";
    options.UnsafeMode = false;
});
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'.

Добавление просмотрщика на страницу

Viewer — обязательное ядро страницы. Его поверхность рендеринга использует два вложенных div:

html
<div id="divDocViewer">
    <div id="div_ctlDoc"></div>
</div>

Относите панель инструментов, монтирования модулей и поверхность Viewer к одной композиции страницы. Поиск и Аннотация внедряют свои встроенные ленты в опциональные монтирования, но эти модули никогда не работают автономно: они всегда присоединяются к Viewer на той же странице. Используйте тот же порядок, что и в Doconut.TestApp и Doconut.TestApp.Distributed:

html
<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 должен быть загружен до скриптов просмотрщика:

html
@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
@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‑плагин. Ниже минимальный набор реальных параметров инициализации (не псевдокод):

javascript
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 () {
        // pages are visible; safe to hide a loading spinner here
    },
    // Forward annotation lifecycle events to the embedded ribbon when it is present.
    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 8 устанавливают следующие части вместе на одной странице:

Часть пакетаТребованиеКак подключается
Ресурсы Viewer, монтирование и objViewerТребуетсяОсновной рендерер документа
Панель инструментов ViewerТребуется в ссылочной композицииРазметка хоста; кнопки вызывают тот же objViewer
Лента поискаОпциональный, лицензированный модульdoconutSearchBar(...).attach(objViewer)
Лента аннотацийОпциональный, лицензированный модульdoconutAnnotationBar(...).attach(objViewer)

Хотя основная панель инструментов Viewer — разметка хоста, она устанавливается вместе с Viewer и никогда не должна документироваться как отдельный элемент управления. Это сохраняет её макет, подписи, иконки и правила авторизации под контролем вашего приложения, в то время как каждая кнопка управляет тем же экземпляром Viewer:

html
<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"> вместе при копировании полной демонстрационной реализации.

Соблюдайте порядок инициализации пакета, используемый в обоих справочных приложениях:

  1. Вывести ресурсы Viewer, Поиска и Аннотации вместе.
  2. Отрисовать панель инструментов Viewer, монтирования лент и монтирование Viewer вместе.
  3. Сначала инициализировать docViewer.
  4. Создать каждую лицензированную ленту и присоединить её к тому же objViewer.
  5. Открыть документ и сохранить его токен для запросов модулей.

Doconut.TestApp.Distributed сохраняет эту точную композицию UI и тот же вспомогательный скрипт панели Viewer. Его дополнительное значение запроса access и настройки повторных попыток асинхронного рендера относятся к распределённому транспорту; они не меняют способ сборки Viewer, панели инструментов или лент.

Защита на стороне сервера важна: когда опциональная возможность недоступна, её скрипт не выводится, и функция jQuery‑плагина не существует.

html
<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 открывает документ и возвращает токен сессии.

csharp
app.MapPost("/api/open", async (Viewer viewer) =>
{
    // The token is opaque — hand it to the widget, never log or persist it.
    string token = await viewer.OpenDocumentAsync("wwwroot/files/Sample.pdf");
    return Results.Ok(new { token });
});

Клиент получает этот токен и передаёт его виджету через objViewer.View(token):

javascript
fetch('/api/open', { method: 'POST' })
    .then(resp => resp.json())
    .then(data => {
        currentToken = data.token;
        objViewer.View(currentToken);
    });

Закрытие документа

Вызовите objViewer.Close(), когда пользователь покидает просмотрщик или открывает заменяющий документ. При серверных сценариях viewer.CloseDocument(token) сразу удаляет кэшированную сессию, освобождает движок рендеринга, удаляет маркер безопасности и аннулирует токен. Скользящее истечение срока действия в конечном итоге выполняет ту же очистку, но явное закрытие рекомендуется для больших документов.

Завершённый поток запросов выглядит так:

text
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 находит лицензию и какие изменения происходят после её обнаружения.

Была ли эта страница полезной?