Szybki start

Renderuj swój pierwszy dokument w kilka minut

Ten przewodnik prowadzi aplikację ASP.NET Core od pustego Program.cs do dokumentu renderowanego w przeglądarce: rejestracja serwera, kompletny pakiet Viewer (pasek narzędzi Viewer, montaż Viewer oraz opcjonalne wstążki Search/Annotation), odwołania do zasobów, inicjalizacja klienta, otwieranie dokumentu i wykonanie.

Konfiguracja serwera

AddDoconut() rejestruje usługi; UseDoconutResources() i UseDoconut() podłączają middleware. Wywołanie zasobów musi być pierwsze. Wywołania sesji są również wymagane — domyślne zabezpieczenie dokumentu Doconut weryfikuje każde żądanie strony względem stanu sesji ASP.NET. Czy już zarejestrowano Doconut podczas Instalacja? Przejdź do następnej sekcji.

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();

Do układu ścieżek w stylu produkcyjnym, zamapuj middleware dokumentu na wyraźną gałąź i utrzymaj cztery ustawienia ścieżek w zgodzie:

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 jest wartością koordynacyjną; nie mapuje ona gałęzi ASP.NET Core samodzielnie. W tym przykładzie host mapuje /doconut, więc klient musi używać BasePath: '/doconut'. ResourcesPath udostępnia wbudowany pakiet pod /doconut-res, a ścieżka zasobów obrazów widżetu to ResPath: '/doconut-res/images'.

Dodaj podgląd do strony

Viewer jest wymaganą podstawą strony. Jego powierzchnia renderowania używa dwóch zagnieżdżonych divów:

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

Traktuj pasek narzędzi, montowanie modułów i powierzchnię Viewer jako jedną kompozycję strony. Search i Annotation wstrzykują swoje wbudowane wstążki do opcjonalnych montowań, ale te moduły nigdy nie są samodzielne: zawsze przyłączają się do Viewer na tej samej stronie. Użyj takiej samej kolejności jak w Doconut.TestApp i 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>

Odwołanie do zasobów podglądu

W widoku Razor wstrzyknięta usługa Viewer generuje znaczniki <link> i <script> podglądu w kolejności zależności — widżet jest wtyczką jQuery, więc jQuery musi być załadowane przed skryptami podglądu:

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
}))

Aby uzyskać kompletny pakiet Viewer, żądaj zasobów Viewer i modułów razem:

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 i IncludeViewerScripts są obowiązkowymi flagami rdzenia. Nigdy nie publikuj przykładu wstążki Search lub Annotation bez nich, montażu Viewer i instancji docViewer. ReferenceCss i ReferenceScripts pomijają zasoby opcjonalnego modułu, gdy bieżąca licencja nie przyznaje tej funkcji; podstawowy Viewer nadal się uruchamia.

Inicjalizacja podglądu

Widżet po stronie klienta jest wtyczką jQuery. Oto minimalny zestaw rzeczywistych opcji inicjalizacji (nie pseudokod):

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);
    }
});

Zapisy opcji są naprawdę mieszane — showThumbs, autoLoad i pageZoom są w camelCase, ale FitType, BasePath i ResPath są w PascalCase. Nie ma spójnej reguły; błędne użycie wielkości liter powoduje, że opcja jest cicho ignorowana (widżet powraca do domyślnej wartości zamiast zgłaszać błąd).

Złożenie kompletnego pakietu Viewer

Obie aplikacje referencyjne .NET 6 instalują następujące części razem na jednej stronie:

Część pakietuWymaganieJak jest połączona
Zasoby Viewer, montaż i objViewerWymaganeRdzeniowy renderujący dokument
Pasek narzędzi ViewerWymagany w kompozycji referencyjnejMarkup hosta; przyciski wywołują ten sam objViewer
Wstążka SearchOpcjonalny, licencjonowany modułdoconutSearchBar(...).attach(objViewer)
Wstążka AnnotationOpcjonalny, licencjonowany modułdoconutAnnotationBar(...).attach(objViewer)

Chociaż główny pasek narzędzi Viewer jest markupiem hosta, jest instalowany razem z Viewer i nigdy nie powinien być dokumentowany jako odrębny kontroler. To utrzymuje jego układ, etykiety, ikony i zasady autoryzacji pod kontrolą Twojej aplikacji, podczas gdy każdy przycisk steruje tą samą instancją 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>

Pełny pasek narzędzi referencyjny kopiuje również wwwroot/js/viewerToolbar.js do aplikacji hosta w celu obsługi obrotu, miniatur, drukowania, pełnego ekranu, układu i pomocników stanu przycisków. Załaduj ten plik hosta po Viewer.ReferenceScripts(...). Trzymaj pomocnika i jego markup <nav id="toolbar"> razem przy kopiowaniu pełnej implementacji demo.

Zachowaj kolejność inicjalizacji pakietu używaną w obu aplikacjach referencyjnych:

  1. Wygeneruj CSS dla Viewer i licencjonowanych modułów.
  2. Renderuj pasek narzędzi Viewer, montowania Search/Annotation oraz montowanie Viewer razem.
  3. Wygeneruj skrypty dla Viewer i licencjonowanych modułów.
  4. Załaduj viewerToolbar.js aplikacji hosta.
  5. Zainicjalizuj docViewer i zachowaj powstały objViewer.
  6. Zainicjalizuj każdą licencjonowaną wstążkę Search lub Annotation.
  7. Wywołaj attach(objViewer) na każdej wstążce.
  8. Otwórz dokument i zachowaj jego token dla żądań Viewer i modułów.

Doconut.TestApp.Distributed zachowuje tę samą kompozycję UI i ten sam pomocnik paska narzędzi Viewer. Jego dodatkowa wartość żądania access oraz ustawienia ponawiania asynchronicznego renderowania należą do transportu rozproszonego; nie zmieniają one sposobu składania Viewer, paska narzędzi ani wstążek.

Zabezpieczenia po stronie serwera są ważne: gdy opcjonalna funkcja jest niedostępna, jej skrypt nie jest generowany, więc funkcja wtyczki jQuery nie istnieje.

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>

Oba wbudowane komponenty generują własny DOM wstążki. Search zawiera grupy Find, Options i Results. Annotation zawiera narzędzia do tworzenia, kontrolki stylu, akcje zapisu oraz opcjonalne akcje eksportu/obrazu. Paski udostępniają open(), close(), reset() i isOpen(); zawsze wywołuj attach(objViewer) raz po ich utworzeniu.

Przykład powyżej pomija opcjonalne wywołania zwrotne hosta oraz końcowe punkty eksportu/obrazu Annotation, aby utrzymać uruchamianie w minimalnym zakresie. Zobacz Wyszukiwanie i Adnotacje dla pełnej konfiguracji funkcji, lub Niestandardowe motywy aby stylizować lub zastąpić pasek narzędzi Viewer zarządzany przez hosta.

Otwórz dokument

Po stronie serwera jest jeden punkt końcowy: wstrzyknięta usługa Viewer otwiera dokument i zwraca token sesji.

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 });
});

Klient pobiera ten token i przekazuje go widżetowi za pomocą objViewer.View(token):

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

Zamknij dokument

Wywołaj objViewer.Close() gdy użytkownik opuszcza podgląd lub otwiera zastępczy dokument. W przepływach sterowanych po stronie serwera, viewer.CloseDocument(token) natychmiast usuwa buforowaną sesję, zwalnia silnik renderujący, usuwa jego znacznik bezpieczeństwa i unieważnia token. Przesuwające się wygaśnięcie ostatecznie wykonuje tę samą czynność, ale jawne zamknięcie jest zalecane przy dużych dokumentach.

Pełny przepływ żądania wygląda następująco:

text
AddDoconut + middleware
    -> render CSS/scripts and mount div
    -> initialize docViewer
    -> OpenDocumentAsync
    -> return opaque token
    -> objViewer.View(token)
    -> page/search/annotation requests
    -> Close / CloseDocument

Traktuj token jak poświadczenie typu bearer: nigdy go nie loguj, nigdy nie przechowuj, przekazuj go wyłącznie widżetowi. Identyfikuje on aktywną sesję dokumentu na serwerze i przestaje działać po wygaśnięciu tej sesji — ponownie otwórz dokument, aby uzyskać nowy token.

Uruchom

Umieść plik PDF w wwwroot/files/Sample.pdf, uruchom dotnet run i otwórz stronę, która hostuje widżet. Pierwsza strona renderuje się w podglądzie, z panelem miniatur po lewej. Jeśli tak się nie stanie, zobacz Rozwiązywanie problemów.

Co otrzymujesz bez licencji

Brak licencji nie powoduje wyjątku. Podgląd renderuje się normalnie, ale każda strona zawiera znak wodny oceny. Zobacz Ustawienia licencji aby dowiedzieć się, jak Doconut znajduje licencję i co się zmienia po jej znalezieniu.

Czy ta strona była pomocna?