Szybki start

Wyświetl 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, pełny 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 dokumentów Doconut weryfikuje każde żądanie strony względem stanu sesji ASP.NET. Stan sesji wymaga pamięci podręcznej: AddDistributedMemoryCache() zapewnia jedną. Aplikacja MVC lub Razor Pages już ją rejestruje, ale aplikacja minimal-API nie, i bez niej aplikacja zatrzyma się przy starcie z komunikatem Unable to resolve service for type 'IDistributedCache'. Czy Doconut został już zarejestrowany podczas Installation? Przejdź do następnej sekcji.

csharp
builder.Services.AddDoconut(options =>
{
    options.LicensePath = "Doconut.Viewer.lic";
});
builder.Services.AddDistributedMemoryCache(); // stan sesji wymaga pamięci podręcznej (MVC/Razor Pages już dodają jedną)
builder.Services.AddSession(); // bezpieczeństwo dokumentów Doconut opiera się na stanie sesji ASP.NET

app.UseSession();          // wywołaj UseSession() przed UseDoconut()
app.UseDoconutResources(); // musi być zarejestrowane przed UseDoconut()
app.UseDoconut();

Dla 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, "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 jest wartością koordynacyjną; nie mapuje gałęzi ASP.NET Core samodzielnie. W tym przykładzie host mapuje /doconut, więc klient używa BasePath: 'doconut'. ResourcesPath udostępnia wbudowany pakiet pod /doconut-res, a ścieżka zasobu obrazu widżetu jest więc ResPath: 'doconut-res/images'. Zapisz oba bez początkowego ukośnika: viewer dodaje go sam, więc '/doconut' zamieniłoby się w //doconut/DocImage.axd, co przeglądarka interpretuje jako żądanie do hosta o nazwie doconut, i viewer kończy z błędem "The viewer handler could not be reached".

Dodaj viewer do strony

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

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 dołączają 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 viewer

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

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ć pełny 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 podstawowymi. 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 przeglądarki

Klientowy widget 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',             // bez wiodącego '/'
    ResPath:    'doconut-res/images',  // bez wiodącego '/'
    onViewerReady: function () {
        // strony są widoczne; można tutaj ukryć wskaźnik ładowania
    },
    // Przekaż zdarzenia cyklu życia adnotacji do osadzonej wstążki, gdy jest obecna.
    onAnnLoaded:    () => annBar?.handleAnnLoaded(),
    onAnnSaved:     () => annBar?.handleAnnSaved(),
    onAnnSaveError: () => annBar?.handleAnnSaveError(),
    onAnnClosed:    () => annBar?.handleAnnClosed(),
    onError: function (message) {
        console.error('Doconut viewer error:', message);
    }
});

Casing opcji jest naprawdę mieszany — showThumbs, autoLoad i pageZoom są w camelCase, ale FitType, BasePath i ResPath są w PascalCase. Nie ma spójnej reguły; błędny casing powoduje, że opcja jest cicho ignorowana (widget przełącza się na domyślne ustawienie zamiast zgłosić błąd).

Zbuduj kompletny pakiet Viewer

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

Część pakietuWymaganieJak jest podłączona
Zasoby Viewer, montowanie i objViewerWymaganeGłówny renderujący dokument
Pasek narzędzi ViewerWymagany w kompozycji referencyjnejMarkup hosta; przyciski wywołują ten sam objViewer
Wstążka wyszukiwaniaOpcjonalny, licencjonowany modułdoconutSearchBar(...).attach(objViewer)
Wstążka adnotacjiOpcjonalny, licencjonowany modułdoconutAnnotationBar(...).attach(objViewer)

Mimo że główny pasek narzędzi Viewer jest markupiem hosta, jest instalowany razem z Viewerem i nigdy nie powinien być dokumentowany jako odrębny kontroler. Dzięki temu jego układ, etykiety, ikony i reguły autoryzacji pozostają pod kontrolą Twojej aplikacji, a każdy przycisk steruje tym samym obiektem 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 referencyjny kopiuje także 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 wywołaniu Viewer.ReferenceScripts(...). Trzymaj pomocnika i jego znacznik <nav id="toolbar"> razem przy kopiowaniu pełnej implementacji demo.

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

  1. Emituj CSS dla Viewer i licencjonowanych modułów.
  2. Wyrenderuj pasek narzędzi Viewer, montowania wyszukiwania/adnotacji oraz montowanie Viewer razem.
  3. Emituj skrypty dla Viewer i licencjonowanych modułów.
  4. Załaduj viewerToolbar.js aplikacji hosta.
  5. Zainicjalizuj docViewer i zachowaj wynikowy objViewer.
  6. Zainicjalizuj każdy licencjonowany pasek wyszukiwania lub adnotacji.
  7. Wywołaj attach(objViewer) na każdym pasku.
  8. Otwórz dokument i zachowaj jego token dla żądań Viewer i modułów.

Doconut.TestApp.Distributed utrzymuje tę samą kompozycję UI i ten sam pomocnik paska Viewer. Dodatkowa wartość żądania access oraz ustawienia ponownych prób asynchronicznego renderowania należą do transportu rozproszonego; nie zmieniają one sposobu, w jaki Viewer, pasek narzędzi czy wstążki są składane.

Zabezpieczenia po stronie serwera są ważne: gdy opcjonalna funkcjonalność jest niedostępna, jej skrypt nie jest emitowany, 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 osadzone komponenty generują własny DOM wstążki. Wstążka wyszukiwania zawiera grupy Znajdź, Opcje i Wyniki. Wstążka adnotacji zawiera narzędzia autorstwa, 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.

Powyższy przykład pomija opcjonalne wywołania zwrotne hosta oraz końcowe punkty eksportu/obrazu adnotacji, aby zminimalizować uruchamianie. Zobacz Wyszukiwanie i Adnotacje po pełną konfigurację specyficzną dla funkcji lub Niestandardowe motywy, aby stylizować lub zastąpić pasek narzędzi Viewer zarządzany przez host.

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) =>
{
    // Token jest nieprzezroczysty — przekaż go do widżetu, nigdy nie loguj ani nie przechowuj.
    string token = await viewer.OpenDocumentAsync("wwwroot/files/Sample.pdf");
    return Results.Ok(new { token });
});

Klient pobiera ten token i przekazuje go do widżetu 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 widok lub otwiera zamienny dokument. W przepływach sterowanych po stronie serwera, viewer.CloseDocument(token) natychmiast usuwa buforowaną sesję, zwalnia silnik renderujący, usuwa znacznik bezpieczeństwa i unieważnia token. Przesuwające się wygaśnięcie ostatecznie wykonuje tę samą czynność, ale zaleca się jawne zamknięcie przy dużych dokumentach.

Pełny przepływ żądania to:

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, nie przechowuj, przekazuj wyłącznie widżetowi. Identyfikuje on aktywną sesję dokumentu na serwerze i przestaje działać, gdy sesja wygaśnie — ponownie otwórz dokument, aby uzyskać nowy token.

Uruchom

Umieść plik PDF w wwwroot/files/Sample.pdf, uruchom dotnet run i otwórz stronę, na której znajduje się widżet. Pierwsza strona zostanie wyrenderowana w przeglądarce, z panelem miniatur po lewej stronie. Jeśli tak się nie stanie, zobacz Troubleshooting.

Co otrzymujesz bez licencji

Brak licencji nie powoduje wyjątku. Widżet renderuje się normalnie, ale każda strona zawiera znak wodny oceny. Zobacz License Setup, aby dowiedzieć się, jak Doconut znajduje licencję i co się zmienia, gdy ją znajdzie.

Czy ta strona była pomocna?