Szybki start
Renderuj swój pierwszy dokument w kilka minut
Ten przewodnik przenosi 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 dokumentu Doconut weryfikuje każde żądanie strony względem stanu sesji ASP.NET. Czy już zarejestrowano Doconut podczas Instalacji? Przejdź do następnej sekcji.
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 produkcyjnego układu ścieżek, zamapuj middleware dokumentu na wyraźną gałąź i utrzymaj cztery ustawienia ścieżek w zgodzie:
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 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 przeglądarkę do strony
Viewer jest wymaganą podstawą strony. Jego powierzchnia renderowania używa dwóch zagnieżdżonych elementów div:
<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:
<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łaj się do zasobów przeglądarki
W widoku Razor wstrzyknięta usługa Viewer generuje znaczniki <link> i <script> przeglądarki w kolejności zależności — widżet jest wtyczką jQuery, więc jQuery musi być załadowane przed skryptami przeglądarki:
@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.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 oraz instancji docViewer. ReferenceCss i ReferenceScripts pomijają zasoby opcjonalnego modułu, gdy bieżąca licencja nie przyznaje tej możliwości; podstawowy Viewer nadal się uruchamia.
Inicjalizacja przeglądarki
Widżet po stronie klienta jest wtyczką jQuery. Oto minimalny zestaw rzeczywistych opcji inicjalizacji (nie pseudokod):
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, natomiast 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 przechodzi do wartości domyślnej zamiast zgłaszać błąd).
Zbuduj kompletny pakiet Viewer
Obie aplikacje referencyjne .NET 8 instalują następujące części razem na jednej stronie:
| Część pakietu | Wymaganie | Jak jest połączona |
|---|---|---|
Viewer resources, mount, and objViewer | Wymagane | Podstawowy silnik renderujący dokument |
| Viewer toolbar | Wymagane w kompozycji referencyjnej | Markup hosta; przyciski wywołują ten sam objViewer |
| Search ribbon | Opcjonalny, moduł licencjonowany | doconutSearchBar(...).attach(objViewer) |
| Annotation ribbon | Opcjonalny, moduł licencjonowany | doconutAnnotationBar(...).attach(objViewer) |
Mimo że główny pasek narzędzi Viewer jest markupiem hosta, jest instalowany razem z Viewer 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 tą samą instancją 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>Pełny pasek referencyjny kopiuje także wwwroot/js/viewerToolbar.js do aplikacji hosta w celu obsługi rotacji, miniatur, drukowania, pełnego ekranu, układu i pomocników stanu przycisków. Załaduj ten plik hosta po Viewer.ReferenceScripts(...). Zachowaj pomocnika i jego markup <nav id="toolbar"> razem przy kopiowaniu pełnej implementacji demo.
Zachowaj kolejność inicjalizacji pakietu używaną w obu aplikacjach referencyjnych:
- Emituj zasoby Viewer, Search i Annotation razem.
- Renderuj pasek narzędzi Viewer, montowania wstążek i montowanie Viewer razem.
- Zainicjalizuj najpierw
docViewer. - Utwórz każdą licencjonowaną wstążkę i przyłącz ją do tego samego
objViewer. - Otwórz dokument i zachowaj jego token do żądań modułów.
Doconut.TestApp.Distributed zachowuje tę dokładną kompozycję UI oraz ten sam pomocnik paska Viewer. Jego 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 lub wstążki są składane.
Zabezpieczenia po stronie serwera są ważne: gdy opcjonalna funkcja jest niedostępna, jej skrypt nie jest emitowany, więc funkcja wtyczki jQuery nie istnieje.
<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. 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 endpointy eksportu/obrazu Annotation, aby utrzymać minimalny start. Zobacz Wyszukiwanie i Adnotacje dla pełnej konfiguracji funkcji, lub Niestandardowe motywy aby stylizować lub zastąpić pasek Viewer należący do hosta.
Otwórz dokument
Po stronie serwera jest jeden punkt końcowy: wstrzyknięta usługa Viewer otwiera dokument i zwraca token sesji.
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):
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 przeglądarkę 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 explicite zamknięcie jest zalecane przy dużych dokumentach.
Pełny przepływ żądania wygląda następująco:
AddDoconut + middleware
-> render CSS/scripts and mount div
-> initialize docViewer
-> OpenDocumentAsync
-> return opaque token
-> objViewer.View(token)
-> page/search/annotation requests
-> Close / CloseDocumentTraktuj token jak poświadczenie typu bearer: nigdy go nie loguj, nie przechowuj, przekazuj go wyłącznie widżetowi. Identyfikuje on aktywną sesję dokumentu na serwerze i przestaje działać po wygaśnięciu 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 przeglądarce, 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. Przeglądarka renderuje normalnie, ale każda strona zawiera znak wodny oceny. Zobacz Konfiguracja licencji aby dowiedzieć się, jak Doconut znajduje licencję i co się zmienia po jej znalezieniu.
Czy ta strona była pomocna?