Szybki start
Renderuj swój pierwszy dokument w kilka minut
Ten przewodnik prowadzi aplikację ASP.NET Core od pustego Program.cs do wyświetlenia dokumentu w przeglądarce: rejestracja serwera, pełny pakiet Viewer (pasek narzędzi Viewer, montaż Viewer oraz opcjonalne wstążki Szukaj/Adnotacja), odwołania do zasobów, inicjalizacja klienta, otwarcie dokumentu i wykonanie.
Aplikacja host musi celować w środowisko uruchomieniowe obsługujące netstandard2.1 — .NET Core 3.0 lub nowszy. Aplikacje referencyjne dostarczane z tym wydaniem celują w net8.0.
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. Stan sesji potrzebuje pamięci podręcznej: AddDistributedMemoryCache() ją zapewnia. 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.
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(); // zabezpieczenie dokumentu Doconut opiera się na stanie sesji ASP.NET
app.UseSession(); // wywołaj UseSession() przed UseDoconut()
app.UseDoconutResources(); // musi być zarejestrowane przed UseDoconut()
app.UseDoconut();Aplikacja referencyjna zachowuje historyczny kształt żądania DocImage.axd i montuje middleware dokumentu na pasującej gałęzi. To jest układ do skopiowania, gdy przenosisz istniejącą aplikację .NET Standard, ponieważ klient nadal wywołuje ten sam URL, co zawsze:
builder.Services.AddDoconut(options =>
{
options.UnsafeMode = false; // utrzymaj włączoną kontrolę sesji Doconut
options.ShowDoconutInfo = false;
});
builder.Services.AddDistributedMemoryCache(); // stan sesji wymaga pamięci podręcznej (MVC/Razor Pages już dodają jedną)
builder.Services.AddSession(options =>
{
options.IdleTimeout = TimeSpan.FromSeconds(3600);
options.Cookie.HttpOnly = true;
options.Cookie.IsEssential = true;
});
// Wymagane dla kodowań wielobajtowych w niektórych formatach dokumentów.
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
app.UseStaticFiles();
app.UseRouting();
app.UseSession();
app.UseDoconutResources(); // PRZED gałęzią dokumentu
app.MapWhen(
context => context.Request.Path.Value?.EndsWith("DocImage.axd", StringComparison.OrdinalIgnoreCase) == true,
branch => branch.UseDoconut());UseDoconut() nie przyjmuje opcji w tym wydaniu — wszystko przeniesiono do AddDoconut(). MiddlewarePath jest wartością koordynacyjną; nie mapuje gałęzi ASP.NET Core samodzielnie. ResourcesPath udostępnia osadzony 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 wymaganym rdzeniem 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, montaż modułów i powierzchnię Viewer jako jedną kompozycję strony. Szukaj i Adnotacja wstrzykują swoje osadzone wstążki do opcjonalnych montażów, ale te moduły nigdy nie są samodzielne: zawsze przyłączają się do Viewer na tej samej stronie.
<nav id="toolbar" aria-label="Document viewer controls">
<!-- Nawigacja przeglądarki, przybliżanie, przyciski Szukaj i Adnotacja -->
</nav>
<div id="searchBarMount"></div>
<div id="annBarMount"></div>
<div id="divDocViewer">
<div id="div_ctlDoc"></div>
</div>Odwołanie 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 — widget 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 podstawowymi. Nigdy nie publikuj przykładu wstążki Search lub Annotation bez nich, montowania 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.
Te wywołania zastępują ręcznie kopiowane pliki skryptów. Jeśli aplikacja nadal ładuje własne docViewer.js, documentLinks.js lub docViewer.UI.js, usuń te znaczniki, gdy strona działa z wygenerowanych zasobów — zobacz przewodnik migracji.
Inicjalizacja przeglądarki
Widget 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: '/',
ResPath: 'doconut-res/images',
onViewerReady: function () {
// strony są widoczne; można ukryć wskaźnik ładowania tutaj
},
// Przekaż zdarzenia cyklu życia adnotacji do osadzonego paska, gdy jest obecny.
onAnnLoaded: () => annBar?.handleAnnLoaded(),
onAnnSaved: () => annBar?.handleAnnSaved(),
onAnnSaveError: () => annBar?.handleAnnSaveError(),
onAnnClosed: () => annBar?.handleAnnClosed(),
onError: function (message) {
console.error('Doconut viewer error:', message);
}
});Dwa szczegóły decydują, czy ta strona w ogóle zadziała:
- Wywołania zwrotne są opcjami.
onViewerReady,onViewerBusy,onCopyionAutoLoadStatussą przekazywane w obiekcie opcji. Poprzedni viewer wyprowadzał nazwy globalnych funkcji z identyfikatora kontenera i wywoływał je; ten tego nie robi. Obsługa zadeklarowana wyłącznie jako globalna funkcja nigdy nie zostaje uruchomiona i nic nie jest zgłaszane. ResPathwskazuje prefiks zasobów, a nie korzeń aplikacji. Przy domyślnymResourcesPathwartość todoconut-res/images. Jeśli zmieniłeśResourcesPath, użyj własnego prefiksu.
Oba te przypadki kończą się cichym niepowodzeniem — zobacz Rozwiązywanie problemów oraz przewodnik migracji.
Wielkość liter opcji jest naprawdę mieszana — showThumbs, autoLoad i pageZoom są w stylu camelCase, ale FitType, BasePath i ResPath są w stylu PascalCase. Nie ma spójnej reguły; jeśli użyjesz niewłaściwej wielkości liter, opcja zostanie cicho zignorowana (widget powróci do wartości domyślnej zamiast zgłosić błąd).
Zbudowanie pełnego pakietu Viewer
Obie aplikacje referencyjne instalują następujące części razem na jednej stronie:
| Część pakietu | Wymaganie | Jak jest połączona |
|---|---|---|
Zasoby Viewer, montowanie i objViewer | Wymagane | Rdzeniowy renderujący dokument |
| Pasek narzędzi Viewer | Wymagany w kompozycji referencyjnej | Markup hosta; przyciski wywołują ten sam objViewer |
| Wstążka wyszukiwania | Opcjonalny, moduł licencjonowany | doconutSearchBar(...).attach(objViewer) |
| Wstążka adnotacji | Opcjonalny, moduł licencjonowany | 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. 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>Zachowaj kolejność inicjalizacji pakietu używaną w obu aplikacjach referencyjnych:
- Wygeneruj CSS dla Viewer i licencjonowanych modułów.
- Wyrenderuj pasek narzędzi Viewer, montowania Search/Annotation oraz montowanie Viewer razem.
- Wygeneruj skrypty dla Viewer i licencjonowanych modułów.
- Załaduj własny skrypt pomocniczy paska narzędzi aplikacji hosta.
- Zainicjalizuj
docVieweri zachowaj powstałyobjViewer. - Zainicjalizuj każdą licencjonowaną wstążkę Search lub Annotation.
- Wywołaj
attach(objViewer)na każdej wstążce. - Otwórz dokument i zachowaj jego token dla żądań Viewer i modułów.
Ochrony po stronie serwera są istotne: gdy opcjonalna funkcjonalność jest niedostępna, jej skrypt nie jest generowany, 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. Wstążka Search zawiera grupy Find, Options i Results. Wstążka Annotation zawiera narzędzia autorskie, 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 utrzymać minimalny rozruch. 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. Otwieranie jest asynchroniczne w tym wydaniu.
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):
fetch('/api/open', { method: 'POST' })
.then(resp => resp.json())
.then(data => {
currentToken = data.token;
objViewer.View(currentToken);
});Nie ma nic do wstępnego przygotowania przed pierwszym otwarciem. Jeśli przenosisz aplikację dalej, usuń każde wywołanie InitCache() oraz flagę go chroniącą — metoda już nie istnieje.
Zamknij dokument
Wywołaj objViewer.Close(), gdy użytkownik opuszcza widżet lub otwiera zastępczy dokument. W przepływach sterowanych po stronie serwera, viewer.CloseDocument(token) natychmiast usuwa pamięć podręczną sesji, 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 zaleca się jawne zamknięcie przy dużych dokumentach.
Pełny przepływ żądania wygląda następująco:
AddDoconut + middleware
-> renderuje CSS/skrypty i montuje div
-> inicjalizuje docViewer
-> OpenDocumentAsync
-> zwraca nieprzezroczysty token
-> objViewer.View(token)
-> żądania stron/wyszukiwania/annotacji
-> Close / CloseDocumentTraktuj 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ć, 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ę, która hostuje widżet. Pierwsza strona jest renderowana w widżecie, 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. Widżet renderuje się normalnie, ale każda strona zawiera znak wodny oceny. Zobacz Konfiguracja licencji, aby dowiedzieć się, jak Doconut znajduje licencję i co się zmienia, gdy ją znajdzie.
Czy ta strona była pomocna?